繁体   English   中英

C#派生类,重载解析

[英]C# derived classes, overload resolution

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

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) { }

我究竟做错了什么?

重载在编译时解决 - 并且您没有DoMethod(Base item)方法 - 因此它无法解析调用。 离开列表和循环,你有效地写:

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

编译器必须找到一个名为DoMethod的方法,该方法适用于Base类型的单个参数。 没有这样的方法,因此失败了。

这里有几个选项:

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

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

    再次,这非常难看

  • 如果可能的话,重新设计你正在做的事情,以便能够使用正常继承

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

您想使用继承和覆盖

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

在Compile time而不是运行时确定调用哪个方法,因此编译器无法知道要调用哪个方法。 您有两个选项:切换对象的类型并调用适当的方法,或者如果您使用的是.NET 4,请使用类型dynamic。

foreach(dynamic o in oList)
{
   DoMethod(o);
}

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

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

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

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

public class Base
{
    public virtual void DoMethod() { ... }
}

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

我不知道所有的细节,但如果它真的不适合继承你可以使用接口。

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

Interface IMethodizable
{
   void DoMethod();
}

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

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

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

由于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: ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM