我有两个类,它们都派生自相同的基类型。
class basetype{}
class TypeA : basetype{}
class TypeB : basetype{}
...
它们中的一些被存储在一个列表中。
List<basetype> myObjects
与往常一样,这些类型中的每一个都必须以不同的方式处理。现在我有两个方法来处理它们,其中一个方法将basetype作为参数。
HandleTypes(TypeA obj){}
HandleTypes(TypeB obj){}
HandleTypes(basetype obj)
目前,我的HandleAllTypes看起来像这样:
string name = obj.GetType().Name
switch(name)
{
case "TypeA":
return HandleTypes(obj as TypeA);
case "TypeB":
return HandleTypes(obj as TypeB);
....
}
现在这是废话。有没有像
这样的方法?HandleTypes(obj ?"as derived type"?)
通过MSDN和其他来源搜索,一无所获。
HandleTypes( obj as dynamic );
?
当我不得不处理第三方类时,我已经使用过几次了。当有很多派生类时,它也会非常有用。
您可以很容易地检查处理函数是否实现:
try {
HandleTypes( obj as dynamic )
}
catch( RuntimeBinderException ex ) {
// not implemented
}
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()
在编译时不可能选择正确的方法,因为它不知道要绑定到哪个方法。你可以使用反射。
对于划分类型,我倾向于这样做:TypeA aRef = obj as TypeA;
if (aRef != null)
HandleTypes(aRef);
然而,理想的方法是使用继承并将HandleType
方法放在基类上,在需要的地方将virtual
和override
方法放在派生类型上。然而,有时出于某种原因,这不是一个选择。
通常,你可能会在basetype/TypeA/TypeB上实现你的'HandleTypes'功能,只是调用obj.HandleTypes()
,并让多态性处理它。有什么理由不能这么做吗?
我认为这里需要的是虚拟方法。
基本上你在基类上声明了一个虚拟方法,比如叫做DoWork()。
现在你可以在TypeA上重写这个虚方法。你也可以在TypeB上重写它。
如果你在一个基对象上调用DoWork(),它的方法将被使用。如果你在一个类型a的对象上调用DoWork(),它的方法将被使用。
基本上,只要在正确的类中重写它,就会使用正确的方法。
更多信息:http://msdn.microsoft.com/en-us/library/aa645767(v=vs.71).aspx
这里有一种不同的方法来获得你想要做的事情。
方法:
void HandleTypes(IEnumerable<Apple> apples)
void HandleTypes(IEnumerable<Banana> banana)
void HandleTypes(IEnumerable<Orange> oranges)
void HandleTypes(IEnumerable<Fruit> fruit)
调用:
List<Fruit> fruitbasket = GetBasket();
HandleTypes(fruitbasket.OfType<Apple>());
HandleTypes(fruitbasket.OfType<Orange>());
HandleTypes(fruitbasket.OfType<Banana>());
HandleTypes(fruitbasket.OfType<Fruit>());
或者被:
调用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;
}
}
你需要的是双重分派,这在c#中是无法直接实现的。基于访问者模式的解决方案可以用来模拟双重分派,通过让basetype
声明一个抽象的Accept
方法来调用访问者,重载结果将选择正确的方法。
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);
}
将你的"处理"方法放在一个从Visitor
派生的类中,可以用普通的重载解析来解决这个问题。在我看来,这是一个简洁的设计比使用反射
你所要求的是不可能的。方法调用在编译时解决,之后不会更改-但是您要求根据运行时值选择特定的方法。这只能使用委托实现,但即使使用委托,也不能将参数上转换为比委托声明中指定的类型更派生的类型。