关于c#:接口中的可选参数,没有默认值

Optional argument in interface without default value

令我惊讶的是,C使用值作为接口的可选方法参数,而不是实现此接口的类的参数。例如:

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
26
27
using System;

public class Program
{
    private static IMyInterface instance;

    public static void Main()
    {
        instance = new MyClass();

        instance.PrintOpt();
        ((MyClass)instance).PrintOpt();
    }
}

public interface IMyInterface
{
    void PrintOpt(bool opt = false);
}

public class MyClass : IMyInterface
{
    public void PrintOpt(bool opt = true)
    {
        Console.WriteLine($"Value of optional argument is {opt}");
    }
}

产生输出:

Value of optional argument is False

Value of optional argument is True

我的问题是-可以在接口中定义一个没有默认值或"可重写"的可选参数,所以对保存在接口类型变量中的实例调用方法时,会使用在实现此接口的类中定义的可选值?


如果您理解了可选参数是如何在内部处理的,这并不奇怪:它们是在编译期间内联的。

换句话说,在调用方法的地方,任何可选参数都由编译器传递——如果您调用的是接口方法,编译器不知道有一个具有不同可选参数的实现。这样的代码最明显的区别是:

1
2
3
4
5
IMyInterface interface = new MyClass();
MyClass theClass = (MyClass)interface;

interface.PrintOpt(); // false
theClass.PrintOpt(); // true

编译成这个(翻译回c):

1
2
interface.PrintOpt(false);
theClass.PrintOpt(true);

在IL代码中,"default"参数不再是"default"——它们只是另一个显式传递的参数。

如果要使用可重写的可选参数,只需使用方法重载。或者更好的方法是,使用不代表任何内容的默认值(例如,nulldefault(int?),并在方法内部对默认值进行任何替换。这与将可选参数包含到C的原始原因是一致的-VB样式的COM接口通常有带有几十个参数的方法,几乎所有这些参数都是可选的。直到现在,当你想调用这样的方法时,你必须做一些类似的事情

1
2
comInterface.MyMethod(TheActualArgumentICareAbout, Type.Missing, Type.Missing,
                      Type.Missing, Type.Missing, ...);

现在你可以做了

1
comInterface.MyMethod(argument, anotherSuperUseful: true);

这种区别是一个大问题——它还意味着您不应该更改任何公共方法的默认参数。任何未经重新编译而使用您的库的用户仍将使用旧的默认值。它类似于处理const值或枚举的方式。如果使用null作为默认值,则实际的默认值将位于方法本身内部,并且所有调用方都将"看到"正确的默认值,即使它们不重新编译(类似于使用public static readonly字段而不是const或使用具有属性而不是枚举的类)。