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

Optional arguments in interface without any default value

令我感到惊讶的是,C# 使用接口的可选方法参数的值,而不是来自实现此接口的 class。例如:

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

我的问题是:是否可以在没有默认值或“可覆盖”的接口中定义可选参数,因此对保存在接口类型变量中的实例调用方法使用定义在[=18中的可选值=] 实现这个接口?

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

换句话说,在调用方法的地方,任何可选参数都由编译器传递 - 如果您正在调用接口方法,编译器不知道有一个 different 可选参数。使用这样的代码可以最好地看出区别:

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

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

编译成这个(翻译回 C#):

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

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

如果你想使用可覆盖的可选参数,只需使用方法重载。或者更好的是,使用没有任何意义的默认值(例如 nulldefault(int?))并在方法内部对默认值进行任何替换。这符合最初将可选参数包含到 C# 的最初原因 - VB 样式的 COM 接口通常具有带有数十个参数的方法,几乎​​所有参数都是可选的。直到现在,当你想调用这样的方法时,你必须做类似

的事情
comInterface.MyMethod(TheActualArgumentICareAbout, Type.Missing, Type.Missing, 
                      Type.Missing, Type.Missing, ...);

现在你可以做

comInterface.MyMethod(argument, anotherSuperUseful: true);

这个区别很重要 - 这也意味着您不应该更改任何 public 方法的默认参数。任何人在没有重新编译的情况下使用你的库仍然会使用旧的默认值。它类似于 const 值或枚举的处理方式。如果您使用 null 作为默认值,实际的默认值将在方法本身内部,并且所有调用者都将 "see" 正确的默认值,即使他们不重新编译(类似于使用 public static readonly 字段而不是 const,或具有属性而不是枚举的 class。