5

I have a couple of classes, all derived from the same base type.

class basetype{}
class TypeA : basetype{}
class TypeB : basetype{}
...

A number of them is stored in a list.

List<basetype> myObjects

As always, each of these types has to be handled differently. Now I have a couple of methods to handle them, and one method that takes the basetype as parameter.

HandleTypes(TypeA obj){}
HandleTypes(TypeB obj){}
HandleTypes(basetype obj)

currently, my HandleAllTypes looks like that:

string name = obj.GetType().Name
switch(name)
{
  case "TypeA":
    return HandleTypes(obj as TypeA);
  case "TypeB":
    return HandleTypes(obj as TypeB);
  ....
}

now this is crap. Is there a way like

HandleTypes(obj ?"as derived type"?)

Searched through MSDN and other sources, found nothing.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
Alexander
  • 145
  • 1
  • 1
  • 6
  • Couldn't you make a `handle()` method on the base type, and then override it in `TypeA` and `TypeB` as appropriate? Then all you have to do is call `obj.handle()` on all the elements in the list. – aroth Jul 27 '11 at 12:48

8 Answers8

7

How about

HandleTypes( obj as dynamic );

?


I've used this a couple times when I had to deal with third-party classes. It also can be very helpful when there are a lot of derived classes.

You can easily check whether the handling function is implemented:

try {
   HandleTypes( obj as dynamic )
}
catch( RuntimeBinderException ex ) {
   // not implemented
}
Bertrand Marron
  • 21,501
  • 8
  • 58
  • 94
4
class basetype{
  public virtual void Handle(){
     // do only for base type 
  }
} 
class TypeA : basetype{
  public override void Handle(){
     // do only for Atype
  }
}
class TypeB : basetype{
  public override void Handle(){
     // do only for Btype
  }
}

foreach(baseType obj in myObjects)
    obj.Handle() 
hungryMind
  • 6,931
  • 4
  • 29
  • 45
1

It won't be possible to choose the correct method like that at compile time, because it won't know which one to bind to. You could possibly use reflection for it.

For splitting out types I tend to do something like this:

TypeA aRef = obj as TypeA;

if (aRef != null)
    HandleTypes(aRef);

However, the ideal way would be to use inheritance and put a HandleType method on the base class, make it virtual and override it on the derived types where needed. However, sometimes this isn't an option for whatever reason.

Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
1

Normally, you'd probably implement your 'HandleTypes' functionality on basetype/TypeA/TypeB, and just call obj.HandleTypes(), and let the polymorphism handle it. Any reason you can't do that?

Flynn1179
  • 11,925
  • 6
  • 38
  • 74
  • The whole application is a WCF WebService. I'm getting a list of object identifiers from the caller and call another (third party) WebService to retrieve the data for each of the objects I got the identifier for. I used "Add Service Reference" to connect to the other service, so I have little to no influence on how the objects are created without changing the generated code. Since the WebService I'm calling is also still work in progress, changing the generated code is less than desirable :) – Alexander Jul 27 '11 at 13:48
  • @Alexander - the generated classes should be partial classes, and you should be able to add the Flynn1179's methods there. – Amy B Jul 27 '11 at 15:14
1

I think what you need here is virtual methods.

Basically you declare a virtual method on your base class, called lets say DoWork().

Now you can override this virtual method on TypeA. You can override it on TypeB as well.

If you call DoWork() on a base object, its method will be used. If you call DoWork() on an object of typeA, its method will be used.

Basically, the correct method will be used, as long as you override it in the correct class.

More info: http://msdn.microsoft.com/en-us/library/aa645767(v=vs.71).aspx

Bertus van Zyl
  • 582
  • 5
  • 17
1

What you need is double dispatch which isn't available directly in C#. A solution based on the visitor pattern can be used to mimic double dispatch, by having basetype declare an abstract Accept method that makes a call into the visitor, where overload resultion will select the correct method.

abstract class basetype
{
  //..
  public abstract void Accept(Visitor v);
  //..
}

class TypeA
{
  //..
  //..
  public override void Accept(Visitor v) { v.Visit(this); }
}

abstract class Visitor
{
  public abstract void Visit(TypeA a);
  public abstract void Visit(TypeB b);
}

Placing your "Handling" methods in a class derived from Visitor makes it possible to have ordinary overload resolution solve the problem. In my opinion this is a cleaner design than using reflection.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
1

Here's a different way to get at what you're trying to do.

Methods:

void HandleTypes(IEnumerable<Apple> apples)
void HandleTypes(IEnumerable<Banana> banana)
void HandleTypes(IEnumerable<Orange> oranges)
void HandleTypes(IEnumerable<Fruit> fruit)

Called by:

List<Fruit> fruitbasket = GetBasket();

HandleTypes(fruitbasket.OfType<Apple>());
HandleTypes(fruitbasket.OfType<Orange>());
HandleTypes(fruitbasket.OfType<Banana>());
HandleTypes(fruitbasket.OfType<Fruit>());

Or called by:

List<Fruit> fruitbasket = GetBasket();
ILookup<Type, Fruit> fruitLookup = fruitBasket.ToLookup(x => x.GetType());

foreach(IGrouping<Type, Fruit> fruitRollup in fruitLookup)
{
  switch(fruitRollup.Key.Name)
  {
    case "Apple" :
      return HandleTypes(fruitRollup.OfType<Apple>());
      break;
    case "Banana" :
      return HandleTypes(fruitRollup.OfType<Banana>());
      break;
    case "Orange" :
      return HandleTypes(fruitRollup.OfType<Orange>());
      break;
    case "Fruit" :
      return HandleTypes(fruitRollup.OfType<Fruit>());
      break;
    default :
      return HandleTypes(fruitRollup.OfType<Fruit>());
      break;
  }
}
Amy B
  • 108,202
  • 21
  • 135
  • 185
0

What you're asking for is not possible. Method calls are resolved at compile time and do not change afterwards - but you're asking to select a particular method based on a runtime value. This would only be possible using delegates, but even with delegates, you will not be able to upcast an argument to a more derived type than the one specified in the delegate declaration.

Mark H
  • 13,797
  • 4
  • 31
  • 45