是否可以在 C# 中限制可选参数的值?
Is it possible to restrict the values of optional parameters in C#?
C# 允许使用 optional parameters:可以在调用中省略参数的情况下指定值,然后编译器自行指定值。
示例:
public interface IFoo {
void SomeMethod (int para = 0);
}
这个想法很有用,但问题是可以在 class 层次结构的不同级别上定义多个 "default values"。示例:
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
.
当然要靠程序员来保持一致性,但是当程序员在过程中his/her改变主意而忘记修改所有默认值时,很容易发生这种精神分裂的情况。
问题是编译器不会警告正在使用不同的默认值,而这可以通过向上移动 class 层次结构来检查。此外,有些人可能会模仿默认参数:
public class SubFoo2 {
public virtual void SomeMethod () {
SomeMethod(1);
}
public void SomeMethod (int para) {
//do something
}
}
这允许动态绑定并因此一致地覆盖。因此,需要非常小心默认值 "implemented".
有没有办法强制(例如使用编译器标志)检查默认值是否一致?如果不是,那么至少有一个警告,说明某些事情并不真正一致。
如果您希望编译时指示某个方法正在更改可选参数的默认值,您将需要使用某种第三方代码分析工具,因为 C# 本身不会提供提供此类限制的任何方式,或在完成时提供任何警告。
作为解决方法,一种选择是避免使用可选参数值,而是使用多个重载。由于您在这里有一个接口,这意味着使用扩展方法,以便在一般情况下仍然定义具有默认值的重载的实现:
public interface IFoo
{
void SomeMethod(int para);
}
public static class FooExtensions
{
public static void SomeMethod(this IFoo foo)
{
foo.SomeMethod(0);
}
}
因此,虽然这种方法技术上仍然允许某人创建名为SomeMethod
的扩展(或实例)方法并且不接受int
参数,它这意味着有人真的需要不遗余力地积极改变 "default value"。它 不需要 接口的实现来提供默认值,这可能会导致它们无意中提供错误的默认值。
定义 const int DefaultPara = 1;
然后使用它来代替硬编码数值。
interface IFoo
{
void SomeMethod (int para = DefaultPara);
}
public class SubFoo : IFoo {
public void SomeMethod (int para = DefaultPara) {
//do something
}
}
好吧,这不是必需的编译时解决方案 - 但您可以为此进行单元测试(我怀疑您正在认真对待单元测试,如果您问这种问题,您会经常 运行 他们)。这个想法是创建像 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.
C# 允许使用 optional parameters:可以在调用中省略参数的情况下指定值,然后编译器自行指定值。
示例:
public interface IFoo {
void SomeMethod (int para = 0);
}
这个想法很有用,但问题是可以在 class 层次结构的不同级别上定义多个 "default values"。示例:
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
.
当然要靠程序员来保持一致性,但是当程序员在过程中his/her改变主意而忘记修改所有默认值时,很容易发生这种精神分裂的情况。
问题是编译器不会警告正在使用不同的默认值,而这可以通过向上移动 class 层次结构来检查。此外,有些人可能会模仿默认参数:
public class SubFoo2 {
public virtual void SomeMethod () {
SomeMethod(1);
}
public void SomeMethod (int para) {
//do something
}
}
这允许动态绑定并因此一致地覆盖。因此,需要非常小心默认值 "implemented".
有没有办法强制(例如使用编译器标志)检查默认值是否一致?如果不是,那么至少有一个警告,说明某些事情并不真正一致。
如果您希望编译时指示某个方法正在更改可选参数的默认值,您将需要使用某种第三方代码分析工具,因为 C# 本身不会提供提供此类限制的任何方式,或在完成时提供任何警告。
作为解决方法,一种选择是避免使用可选参数值,而是使用多个重载。由于您在这里有一个接口,这意味着使用扩展方法,以便在一般情况下仍然定义具有默认值的重载的实现:
public interface IFoo
{
void SomeMethod(int para);
}
public static class FooExtensions
{
public static void SomeMethod(this IFoo foo)
{
foo.SomeMethod(0);
}
}
因此,虽然这种方法技术上仍然允许某人创建名为SomeMethod
的扩展(或实例)方法并且不接受int
参数,它这意味着有人真的需要不遗余力地积极改变 "default value"。它 不需要 接口的实现来提供默认值,这可能会导致它们无意中提供错误的默认值。
定义 const int DefaultPara = 1;
然后使用它来代替硬编码数值。
interface IFoo
{
void SomeMethod (int para = DefaultPara);
}
public class SubFoo : IFoo {
public void SomeMethod (int para = DefaultPara) {
//do something
}
}
好吧,这不是必需的编译时解决方案 - 但您可以为此进行单元测试(我怀疑您正在认真对待单元测试,如果您问这种问题,您会经常 运行 他们)。这个想法是创建像 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.