【问题标题】:C# virtual methods override the return type & method argumentsC# 虚方法覆盖返回类型和方法参数
【发布时间】:2012-12-21 12:24:35
【问题描述】:

我想在抽象类中用确切的名字初始化虚方法。

在类中,这是我可以覆盖的继承者覆盖方法:

  • 基方法的返回类型
  • 方法的参数

为了告诉你,我真正想做的是这样的:

abstract class A
{
    public virtual void Func1() { }
}

class B : A
{
    override string Func1(int a, int b)
    {
         return (a + b).ToString();
    }
}

我知道,C# 需要像在基类中一样使用返回类型/参数,但在这种情况下可能有关键字 new 的一些提示?

【问题讨论】:

  • 是的,有一个关键字new,但它不是覆盖,它只是隐藏了一个成员。
  • B.Func1 不是覆盖而是重载。所以你没有覆盖
  • 您不能使用 new、static 或 virtual 修饰符来修改覆盖方法。
  • 那么继承有什么意义呢?
  • @四十二多态点

标签: c# polymorphism virtual overriding


【解决方案1】:

想一想……它将如何工作?假设你有一个静态类型为A而动态类型为B的引用,如下:

foo(B b) { bar(b); }
bar(A a) { 
}

覆盖背后的想法是,您在 super/base 上调用一个方法,它会解析为被覆盖的实现。按照这种逻辑,您应该能够在bar 中调用a.Func1(),它应该调用B 中的方法:

a.Func1(???)

但这在这里是不可能的,因为缺少参数。

有参数逆变和返回类型协方差的概念; here 是 SO 中的一个有趣的答案,带有指向 Eric Lippert 的帖子的链接,应该是一个有趣的阅读。

【讨论】:

  • 可能采用与以下相同的机制:F#、Lisp、Haskell。因为在那里他们意识到了,但是有一些动态类型。真正的 C# 随着他自己的每个版本变得越来越动态类型。看看我在本 Q 开头对 Servy 评论的回答。
  • 你是说用这些其他语言完全不同的签名作品覆盖?
  • 是的,我已经告诉你了。这些动态语言使用不同的机制来处理方法的签名。
  • 您对C# 中的这项工作有何期待?例如,在我的示例中,您认为可以用什么替换 ??? 并将呼叫分派到 B.Func1?如果a 的动态类型不接受这些参数,那会是运行时错误吗?
  • 是的!你说的对。您可能会使用这些语言的此类技术捕获运行时错误,但您也可能不会捕获。这仅取决于您。
【解决方案2】:

您只能override 准确的名称和参数。你可以new 任何你想要的东西,但这并不是压倒一切的——只是在完全匹配的情况下隐藏。

我认为在您的情况下,您可能想要为所有可能的组合创建重载,或者创建一个单一的基本抽象方法,该方法采用可以包含您想要的参数的单一类型。

例子:

public abstract void Func1( MyArgumentsType input );

现在派生类必须重写一个方法,但您可以将一组健壮的参数传递给可以处理更多场景的方法。

多态在这里对您有利,因为您可以将派生的参数类型传递给具有特定于情况的属性的方法。当然,这需要实现方法理解更多派生的参数类型。

【讨论】:

    【解决方案3】:

    你不能这样做。重写方法需要具有完全相同的签名,包括参数和返回类型。您无法告诉编译器使用不同的参数和返回类型覆盖方法。

    如果你想要多态行为,你需要有兼容的签名。否则编译器不能保证,所需的方法将以多态方式找到。

    没有将签名void Func1()string Func1(int a, int b) 视为多态的可行方法。您可以使用内部传递 lambda 的 out 参数来概括它们,但无论如何 - 要利用多态行为,您需要将它们带到一个共同的签名中。

    【讨论】:

      【解决方案4】:

      您似乎想要实现类似于 ToString 方法的行为,这些方法对不同类型有许多不同的风格(如 .ToString()float.ToString("c"))。

      在这种情况下,ToString 的不同实现不是Object.ToString 的实现,而是基于参数适当解析的派生对象上的常规附加方法。所有派生类最终都有一个虚拟的ToString 方法(来自基类并可能在当前类中实现)和可选的其他类似命名的非虚拟方法。

      请注意,在 C# 结果类型中,更新时会考虑函数解析时间,因此您不能真正拥有 2 个具有相同名称和相同参数的函数,并期望“正确”选择一个。如果两者同时可见,则会出现编译时错误。方法前面的new 关键字仅使该方法对该类(和派生类)可见,并隐藏具有相同签名的基类方法。不幸的是,当一个人使用基类的变量持有派生类来调用这个方法时,这样做几乎保证了很多混乱——尽管努力隐藏了一个来自基类的虚拟变量,但仍将被调用。

      【讨论】:

      • +1 表示“混乱”...我发现 new 很少是个好主意。
      【解决方案5】:

      方法覆盖意味着修改继承成员的虚拟实现,而不是签名。正如您所指出的,new 关键字将隐藏基类对同名成员的实现。但是它只是可选的,看看这个
      benefit of using new keyword in derived class member having same name with base class member

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-09-08
        • 1970-01-01
        • 1970-01-01
        • 2012-02-19
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        • 2015-05-22
        相关资源
        最近更新 更多