5

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

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

其中一些存储在列表中。

List<basetype> myObjects

与往常一样,这些类型中的每一种都必须以不同的方式处理。现在我有几种方法来处理它们,还有一种方法将基类型作为参数。

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

4

8 回答 8

7

怎么样

HandleTypes( obj as dynamic );

?


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

您可以轻松检查是否实现了处理功能:

try {
   HandleTypes( obj as dynamic )
}
catch( RuntimeBinderException ex ) {
   // not implemented
}
于 2011-07-27T12:54:20.880 回答
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() 
于 2011-07-27T12:58:00.393 回答
1

在编译时不可能像那样选择正确的方法,因为它不知道要绑定到哪一个。你可以使用反射来实现它。

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

TypeA aRef = obj as TypeA;

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

然而,理想的方法是使用继承并将一个HandleType方法放在基类上,在需要的地方将它virtualoverride它放在派生类型上。但是,有时无论出于何种原因,这都不是一种选择。

于 2011-07-27T12:48:58.900 回答
1

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

于 2011-07-27T12:49:55.347 回答
1

我认为您在这里需要的是虚拟方法。

基本上,您在基类上声明一个虚拟方法,称为 DoWork()。

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

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

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

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

于 2011-07-27T12:57:57.503 回答
1

您需要的是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,可以让普通的重载解决方案解决问题。在我看来,这是比使用反射更简洁的设计。

于 2011-07-27T12:58:42.287 回答
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;
  }
}
于 2011-07-27T12:59:18.690 回答
0

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

于 2011-07-27T12:58:11.193 回答