接口中的可选参数,没有默认值

Dom*_*alo 6 c# optional-parameters

我很惊讶C#使用来自接口的可选方法参数的值,而不是来自实现此接口的类.例如:

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}");
    }
}
Run Code Online (Sandbox Code Playgroud)

产生输出:

可选参数的值为False

可选参数的值为True

我的问题是 - 可以在接口中定义一个没有默认值或"overridable"的可选参数,因此在接口类型变量中保存的实例上调用方法会使用在强制接口的类中定义的可选值吗?

Lua*_*aan 10

如果你理解了如何在内部处理可选参数,这就不足为奇了:它们在编译期间被内联.

换句话说,在调用方法的地方,编译器传递任何可选参数 - 如果您正在调用接口方法,则编译器不知道存在具有不同可选参数的实现.使用这样的代码可以看出差异:

IMyInterface interface = new MyClass();
MyClass theClass = (MyClass)interface;

interface.PrintOpt(); // false
theClass.PrintOpt(); // true
Run Code Online (Sandbox Code Playgroud)

哪个编译为此(转换回C#):

interface.PrintOpt(false);
theClass.PrintOpt(true);
Run Code Online (Sandbox Code Playgroud)

"默认"参数在IL代码中不再是"默认" - 它们只是另一个显式传递的参数.

如果要使用可覆盖的可选参数,只需使用方法重载.或者更好的是,使用不代表任何内容的默认值(例如null或default(int?)),并对方法内部的默认值进行任何替换.这与首先​​在C#中包含可选参数的原始原因相一致 - VB风格的COM接口通常具有包含许多参数的方法,几乎​​所有参数都是可选的.到现在为止,当你想要调用这样的方法时,你必须做类似的事情

comInterface.MyMethod(TheActualArgumentICareAbout, Type.Missing, Type.Missing, 
                      Type.Missing, Type.Missing, ...);
Run Code Online (Sandbox Code Playgroud)

现在你可以做到

comInterface.MyMethod(argument, anotherSuperUseful: true);
Run Code Online (Sandbox Code Playgroud)

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