C#允许使用optional parameters:如果在调用中省略了参数,则可以指定值,然后编译器指定值本身。
示例:
public interface IFoo {
void SomeMethod (int para = 0);
}
这个想法很有用,但问题是可以在类层次结构的不同级别定义几个“默认值”。例如:
public class SubFoo : IFoo {
public void SomeMethod (int para = 1) {
//do something
}
}
如果有人打电话:
SubFoo sf = new SubFoo ();
sf.SomeMethod ();
Foo f = sf;
f.SomeMethod ();
结果是第一次调用是在para
等于1
的情况下完成的,第二次调用是para
等于0
(作为接口)。这是有道理的,因为编译器添加了默认值,在第一种情况下,this
是SubFoo
,因此默认值为1
。
程序员当然应该保持一致性,但是当程序员在流程中间改变主意并忘记修改所有默认值时,很容易发生这种精神分裂症状。
问题在于编译器不会警告正在使用不同的默认值,而这可以通过向上移动类层次结构来检查。更有些人可能会使用以下方式模仿默认参数:
public class SubFoo2 {
public virtual void SomeMethod () {
SomeMethod(1);
}
public void SomeMethod (int para) {
//do something
}
}
允许动态绑定,从而一致地覆盖。因此,需要非常小心地如何“实施”默认值。
是否有办法强制执行(例如使用编译器标志)来检查默认值是否一致?如果没有,那么至少要警告某些事情并不是真的一致会很好。
答案 0 :(得分:2)
如果您希望编译时指示方法正在更改可选参数的默认值,那么您将需要使用某种第三方代码分析工具,因为C#本身不提供任何参数提供此类限制的方式,或完成时的任何警告。
作为一种解决方法,一种选择是避免使用可选参数值,而是使用多个重载。由于这里有一个接口,这意味着使用扩展方法,以便在一般情况下仍然定义具有默认值的重载的实现:
public interface IFoo
{
void SomeMethod(int para);
}
public static class FooExtensions
{
public static void SomeMethod(this IFoo foo)
{
foo.SomeMethod(0);
}
}
因此,虽然这种方法技术仍然允许某人创建名为SomeMethod
的扩展(或实例)方法并且不接受int
参数,但这意味着有人会真的需要不遗余力地积极改变“默认值”。它不要求接口的实现提供默认值,这可能会无意中提供错误的默认值。
答案 1 :(得分:2)
没有必要的编译时解决方案 - 但你可以为此进行单元测试(我怀疑你正在认真对待单元测试,如果你提出这类问题,你经常运行它们)。我们的想法是创建类似AssertThatDefaultParametersAreEqual(Type forType)
的断言方法 - 找到所有不是abstract
的类(使用反射)并从forType
继承,然后迭代所有定义了默认参数的方法:
MethodInfo[] methodInfo = Type.GetType(classType).GetMethods(BindingFlags.OptionalParamBinding | BindingFlags.Invoke);
按MethodInfo.Name
对它们进行分组,并检查组内是否所有具有默认值的相同参数(可由MethodInfo.GetParameters().Where(x => x.IsOptional)
获得)具有ParameterInfo.DefaultValue
的相等属性。
编辑:顺便说一句。可能在Mono中不起作用,因为编译器没有义务发出例如:Optional
BindingFlag。
答案 2 :(得分:0)
定义const int DefaultPara = 1;
,然后使用它代替硬编码数值。
interface IFoo
{
void SomeMethod (int para = DefaultPara);
}
public class SubFoo : IFoo {
public void SomeMethod (int para = DefaultPara) {
//do something
}
}