5

好的,我有一些从基类派生的不同对象,我已经将它们中的一堆放在了一个列表中。我想遍历列表并将每个推送到一个方法。我对每个人的类型签名都有单独的方法,但是编译器在抱怨。有人可以解释为什么吗?这是使用泛型的机会吗?如果是,如何使用?

class Base { }
class Level1 : Base { }
class Level2 : Level1 { }

...

List<Base> oList = new List<Base>();
oList.Add(new Level1());
oList.Add(new Level2());

...

...
foreach(Base o in oList)
{
   DoMethod(o);
}

...

void DoMethod(Level1 item) { }
void DoMethod(Level2 item) { }

我究竟做错了什么?

4

8 回答 8

8

重载是在编译时解决的——你没有DoMethod(Base item)方法——所以它不能解决调用。离开列表和循环,你实际上是在写:

Base o = GetBaseFromSomewhere();
DoMethod(o);

编译器必须找到一个DoMethod适用于 type 的单个参数的方法Base。没有这种方法,因此失败。

这里有几个选项:

  • 正如 Markos 所说,您可以在 C# 4 中使用动态类型来使 C# 编译器在执行时使用所引用的对象的实际类型来应用重载。o
  • 您可以使用访客模式有效地获得双重调度(我从来不喜欢这个)
  • 您可以使用asis

    Level1 x = o as Level2;
    if (x != null)
    {
        DoMethod(x); // Resolves to DoMethod(Level1)
    } 
    else
    {
        Level2 y = o as Level2;
        if (y != null)
        {
            DoMethod(y); // Resolves to DoMethod(Level2)
        }
    }
    

    再一次,这很丑陋

  • 如果可能的话,重新设计你正在做的事情以便能够使用正常的继承
于 2010-08-05T20:36:32.243 回答
3

重载方法使用变量的静态类型而不是运行时类型。

您想使用继承和覆盖

class Base { public virtual void DoMethod() { /* ... */  } }
class Level1 : Base { public override void DoMethod() { /* ... */ } }
class Level2 : Level1 { public override void DoMethod() { /* ... */ } }
于 2010-08-05T20:37:17.527 回答
2

调用哪个方法是在编译时而不是运行时确定的,因此编译器无法知道调用哪个方法。您有 2 个选择:切换对象的类型并调用适当的方法,或者如果您使用的是 .NET 4,则使用类型动态。

foreach(dynamic o in oList)
{
   DoMethod(o);
}
于 2010-08-05T20:37:37.123 回答
2

您没有 DoMethod(Base item) 方法。重载不是多态的。这通常通过使用虚拟方法来完成:

class Base {
    public virtual void DoMethod() {...}
}
class Level1 : Base {
    public override void DoMethod() {...}
}
// etc..

foreach(Base o in oList)
{
    o.DoMethod();
}
于 2010-08-05T20:38:53.343 回答
2

由于 C# 7.0 模式匹配是另一种选择。

有关详细信息,请参阅MSDN。您的代码会喜欢:

switch(o)
{
    case Level2 level2: Do(level2); break;
    case Level1 level1: Do(level1); break;
    case Base @base: Do(@base); break;
    default: ...
    case null: ...
}
于 2019-02-13T08:33:55.503 回答
1

在您的 foreach 循环中,o具有类型Base并且两个DoMethod重载都没有Base实例。如果可能,您应该在两个子类中移动DoMethodBase覆盖它:

public class Base
{
    public virtual void DoMethod() { ... }
}
于 2010-08-05T20:39:40.193 回答
1

为了扩展 Mark 的答案,DoMethod 应该是 Base 中的一个虚拟方法,您可以在列表中的每个项目上调用它。

于 2010-08-05T20:39:43.250 回答
0

我不知道所有细节,但如果这是一种确实不适合继承的情况,你可以使用接口来代替。

声明接口,在每个类上实现它,然后你就可以直接转换到接口并从那里运行函数。我的 C# 有点不稳定,但类似,

Interface IMethodizable
{
   void DoMethod();
}

class Level1 : IMethodizable {
  void DoMethod(){
    //insert code here
  }
}

class Level2 : IMethodizable {
  void DoMethod(){
    //insert code here
  }
}

如果类的唯一共同点是该方法,则此方法特别有效。这与在基类中有一个虚拟化方法并覆盖它非常相似。所以这种模式只有在你不应该继承的情况下才会更好,否则 DoMethod 也必须在其他不从 base 继承的对象上运行,等等。

于 2010-08-05T21:22:21.717 回答