在c#中向上转换,并根据派生类型调用特定的方法



我有两个类,它们都派生自相同的基类型。

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方法放在基类上,在需要的地方将virtualoverride方法放在派生类型上。然而,有时出于某种原因,这不是一个选择。

通常,你可能会在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派生的类中,可以用普通的重载解析来解决这个问题。在我看来,这是一个简洁的设计比使用反射

你所要求的是不可能的。方法调用在编译时解决,之后不会更改-但是您要求根据运行时值选择特定的方法。这只能使用委托实现,但即使使用委托,也不能将参数上转换为比委托声明中指定的类型更派生的类型。

相关内容

  • 没有找到相关文章

最新更新