接口中的可选参数没有任何默认值
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" - 它们只是另一个显式传递的参数。
如果你想使用可覆盖的可选参数,只需使用方法重载。或者更好的是,使用没有任何意义的默认值(例如 null
或 default(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。
令我感到惊讶的是,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" - 它们只是另一个显式传递的参数。
如果你想使用可覆盖的可选参数,只需使用方法重载。或者更好的是,使用没有任何意义的默认值(例如 null
或 default(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。