一尘不染

重写方法上的C#可选参数

c#

似乎在.NET Framework中,重写该方法时,可选参数存在问题。以下代码的输出是:“ bbb”“ aaa”。但是我期望的输出是:“ bbb”“
bbb”。是否有解决方案?我知道可以通过方法重载来解决,但想知道这样做的原因。该代码在Mono中也可以正常工作。

class Program
{
    class AAA
    {
        public virtual void MyMethod(string s = "aaa")
        {
            Console.WriteLine(s);
        }

        public virtual void MyMethod2()
        {
            MyMethod();
        }
    }

    class BBB : AAA
    {
        public override void MyMethod(string s = "bbb")
        {
            base.MyMethod(s);
        }

        public override void MyMethod2()
        {
            MyMethod();
        }
    }

    static void Main(string[] args)
    {
        BBB asd = new BBB();
        asd.MyMethod();
        asd.MyMethod2();
    }
}

阅读 219

收藏
2020-05-19

共1个答案

一尘不染

这里需要注意的一件事是,每次都会调用覆盖的版本。将替代更改为:

public override void MyMethod(string s = "bbb")
{
  Console.Write("derived: ");
  base.MyMethod(s);
}

输出为:

derived: bbb
derived: aaa

类中的方法可以执行以下一项或多项操作:

  1. 它定义了其他代码调用的接口。
  2. 它定义了一个在调用时执行的实现。

它可能不会两者都做,因为抽象方法只会做前者。

BBB调用MyMethod()调用方法 定义AAA

由于中存在覆盖BBB,因此调用该方法将导致实现BBB被调用。

现在,中的定义AAA将两件事告知调用代码(嗯,还有一些无关紧要的地方)。

  1. 签名void MyMethod(string)
  2. (对于支持该语言的语言)单个参数的默认值为"aaa",因此,MyMethod()如果在找不到该方法匹配的形式的形式的代码时MyMethod(),可以用对MyMethod(“ aaa”)的调用来替换它。

因此,这就是调用的BBB作用:编译器看到对的调用MyMethod(),未找到方法,MyMethod()但确实找到了方法MyMethod(string)。它还可以看到在定义它的地方有一个默认值“
aaa”,因此在编译时它将其更改为对的调用MyMethod("aaa")

从内部BBB,即使在中被覆盖,也AAA被认为AAA是定义方法的地方BBB,以便 可以 覆盖它们。

在运行时,MyMethod(string)使用参数“ aaa”调用。因为存在一个覆盖的形式,所以该形式被调用,但是不使用“
bbb”来调用它,因为该值与运行时实现无关,而与编译时定义无关。

添加this.更改将检查哪个定义,从而更改调用中使用的参数。

编辑:为什么这对我来说似乎更直观。

就个人而言,由于我所说的是直观的东西,因此只能是个人的,我发现它更直观,原因如下:

如果我正在编码,BBB那么无论是调用还是覆盖MyMethod(string),我都认为这是“正在做的AAA事情”,这是BBB在“正在做的AAA事情”
上做的事情,但实际上AAA都是在做事情。因此,无论是调用还是覆盖,我都将意识到它就是AAA定义的事实MyMethod(string)

如果我要调用使用过的代码BBB,我会想到“使用BBB东西”。我可能不太了解最初是在中定义的AAA,因此我可能会认为这只是实现细节(如果我也没有使用AAA附近的接口)。

编译器的行为符合我的直觉,这就是为什么当初读这个问题时,我觉得Mono有一个错误。经考虑,我看不到任何一个如何比另一个更好地实现指定的行为。

尽管如此,尽管如此,在保持个人水平的同时,我绝不会将可选参数与抽象,虚拟或重写方法一起使用,并且如果要覆盖其他人使用的参数,我会匹配它们。

2020-05-19