问题
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.
回答1:
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
}
回答2:
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()
回答3:
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.
回答4:
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?
回答5:
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
回答6:
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;
}
}
回答7:
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.
回答8:
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.
来源:https://stackoverflow.com/questions/6844508/up-casting-in-c-sharp-and-call-a-specific-method-based-on-the-derived-type