我很惊讶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}");
}
}
产生输出:
可选参数的值为False
可选参数的值为True
我的问题是 - 可以在接口中定义一个没有默认值或“overridable”的可选参数,因此在接口类型变量中保存的实例上调用方法会使用在强制接口的类中定义的可选值吗?
答案 0 :(得分:10)
如果您了解如何在内部处理可选参数,那么这并不令人惊讶:它们在编译期间内联。
换句话说,在调用方法的地方,编译器传递任何可选参数 - 如果您正在调用接口方法,编译器不知道有一个实现与一个不同的可选参数。使用这样的代码可以看出差异:
IMyInterface interface = new MyClass();
MyClass theClass = (MyClass)interface;
interface.PrintOpt(); // false
theClass.PrintOpt(); // true
编译到此(转换回C#):
interface.PrintOpt(false);
theClass.PrintOpt(true);
"默认"参数不再是"默认"在IL代码中 - 它们只是另一个明确传递的参数。
如果要使用可覆盖的可选参数,只需使用方法重载。或者更好的是,使用不具有任何意义的默认值(例如null
或default(int?)
)并替换方法内的默认值。这与首先在C#中包含可选参数的原始原因相一致 - VB风格的COM接口通常具有包含许多参数的方法,几乎所有参数都是可选的。到现在为止,当你想调用这样的方法时,你必须做类似
comInterface.MyMethod(TheActualArgumentICareAbout, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, ...);
现在你可以做到
comInterface.MyMethod(argument, anotherSuperUseful: true);
这种区别是一个大问题 - 它也意味着你不应该改变任何公共方法的默认参数。任何使用您的库而不重新编译的人仍将使用旧的默认值。它与const
值或枚举的处理方式类似。如果您使用null
作为默认值,则实际默认值将位于方法本身内,并且所有呼叫者将"参见"正确的默认值即使他们没有重新编译(类似于使用public static readonly
字段而不是const
,或者使用属性而不是枚举的类。)