c# – 抽象类中的可选参数覆盖派生

Optional parameter in abstract class overriding derived

本问题已经有最佳答案,请猛点这里访问。

在抽象类中

1
2
3
4
public abstract class base
    {
        abstract public int Func(bool flag = true);
    }

我对派生版本做了更改:

1
2
3
4
public class derived : base
    {
        public override int Func(bool flag = false){}
    }

调试显示编译器使用true作为默认值。我本来以为不是这样的,为什么会这样呢?


该行为在第7.5.1.1节的C语言规范(相应参数,重点是Mine)中有说明:

For each argument in an argument list there has to be a corresponding
parameter in the function member or delegate being invoked. The
parameter list used in the following is determined as follows:

  • For virtual methods and indexers defined in classes, the parameter list is
    picked from the most specific declaration or override of the function
    member, starting with the static type of the receiver, and searching
    through its base classes

在绑定过程中(将方法关联到调用的过程),编译器使用上面所述的规则查找参数列表,然后查找符合该参数列表的一组候选方法。其中最好的一个被选中并绑定到调用。采用此代码:

1
2
BaseClass b = new DerivedClass( );
b.Func( );

在绑定过程中使用的参数列表是在BaseClass.Func中声明的参数列表(因为这是b的静态类型),并且候选方法集是BaseClass.FuncDerivedClass.Func(据说它们是适用的,因为两个参数列表都对应于所选的参数列表)。然后,根据预期,DerivedClass.Func被确定为最佳候选,因此使用BaseClass.Func的参数列表绑定到调用,从而使用flag = true

您可以在7.5.3节(过载分辨率)中找到更多详细信息。最后,如果您想知道,抽象方法被认为是虚拟的,如§10.6.6(抽象方法)所述:

An abstract method declaration introduces a new virtual method but does not provide
an implementation of that method.


我创建了一个模拟您情况的示例。长话短说:使用哪个默认值取决于您是通过基类类型的变量访问实例,还是通过派生类访问实例。因此编译器根据变量类型检测可选参数的默认值。这是有意义的,因为您可以在变量(多态性)中存储派生类的任何实例。甚至在编译时也不清楚实例的类型(例如,如果您将系统配置为使用特定类型)。编译器只知道变量的类型,并且以类型安全的方式放入变量中的任何实例都提供相同的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;

public abstract class MyBase
{
    public abstract bool GetValue(bool value = true);
}

public class MyDerived : MyBase
{
    public override bool GetValue(bool value = false)
    {
        return value;
    }
}

public class Test
{
    public static void Main()
    {
        var derived = new MyDerived();
        Console.WriteLine("Value = {0}", derived.GetValue());
        MyBase myBase = derived;
        Console.WriteLine("Value = {0}", myBase.GetValue());
    }
}

我假设您对基类类型的变量调用该方法,这将解释该行为。
基本上,如果不重写方法,而是对其进行阴影处理,那么这类似于行为。在几乎所有情况下,您都希望避免这种情况,因为代码的行为不符合预期的风险相对较高。
因此,我建议去掉可选参数并使它们成为必需的参数(并且可能在没有参数的情况下创建一个重载)。必须键入较少的内容,并且能够稍后更改默认值,这不值得冒代码出现意外行为的风险。这种行为现在对您来说可能是透明的,但是我怀疑几个月后或者您的同事必须维护代码。