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

时间:2011-07-27 12:45:52

标签: c# reflection casting upcasting

我有几个类,都来自相同的基类型。

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和其他来源搜索,一无所获。

8 个答案:

答案 0 :(得分:6)

怎么样

HandleTypes( obj as dynamic );


当我不得不处理第三方课程时,我已经使用了几次。当有很多派生类时,它也会非常有用。

您可以轻松检查是否已实施处理功能:

try {
   HandleTypes( obj as dynamic )
}
catch( RuntimeBinderException ex ) {
   // not implemented
}

答案 1 :(得分: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() 

答案 2 :(得分:1)

在编译时不可能选择正确的方法,因为它不知道要绑定哪个方法。您可以使用反射。

对于拆分类型,我倾向于做这样的事情:

TypeA aRef = obj as TypeA;

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

但是,理想的方法是使用继承并在基类上放置HandleType方法,在需要的情况下在派生类型上创建virtualoverride。但是,有时无论出于何种原因,这都不是一种选择。

答案 3 :(得分:1)

通常,您可能在basetype / TypeA / TypeB上实现'HandleTypes'功能,只需调用obj.HandleTypes(),然后让多态处理它。你有什么理由不能这样做吗?

答案 4 :(得分:1)

我认为你需要的是虚拟方法。

基本上你在你的基类上声明一个虚拟方法,叫做DoWork()。

现在,您可以在TypeA上覆盖此虚拟方法。 您也可以在TypeB上覆盖它。

如果在基础对象上调用DoWork(),则将使用其方法。 如果在类型A的对象上调用DoWork(),则将使用其方法。

基本上,只要在正确的类中覆盖它,就会使用正确的方法。

更多信息:http://msdn.microsoft.com/en-us/library/aa645767(v=vs.71).aspx

答案 5 :(得分:1)

您需要的是double dispatch,这在C#中无法直接使用。基于visitor pattern的解决方案可以用来模仿双重调度,方法是让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派生的类中,可以使普通的重载解决方案解决问题。在我看来,这比使用反射更清晰。

答案 6 :(得分:1)

这是一种不同的方式来了解你想要做的事情。

方法:

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;
  }
}

答案 7 :(得分:0)

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