【问题标题】:Is it possible to restrict the values of optional parameters in C#?是否可以限制 C# 中可选参数的值?
【发布时间】:2015-03-25 08:39:08
【问题描述】:

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(作为接口)。这是有道理的,因为编译器添加了默认值,在第一种情况下,thisSubFoo,因此默认值为 1

保持一致性当然取决于程序员,但是当程序员在过程中改变主意并忘记修改所有默认值时,很容易发生这种精神分裂的情况。

问题在于编译器不会警告正在使用不同的默认值,而这可以通过向上移动类层次结构来检查。此外,有些人可能会模仿默认参数:

public class SubFoo2 {

    public virtual void SomeMethod () {
        SomeMethod(1);
    }

    public void SomeMethod (int para) {
        //do something
    }

}

这允许动态绑定并因此一致地覆盖。因此,需要非常小心“实现”默认值的方式。

有没有办法强制检查默认值是否一致(例如使用编译器标志)?如果不是这样,最好至少有一个警告,指出某些事情并不是真正一致的。

【问题讨论】:

  • 不,但您可以编写 Roslyn 诊断程序。
  • "忘记修改所有默认值" --- 当这种情况发生时,表明应用程序设计已损坏(或开始损坏)。
  • @zerkms:是的,但正如他们在软件架构中所说:“软件架构的目标是制造万无一失的系统,宇宙的目标是创造更多傻瓜,目前宇宙正在获胜。”.
  • 你的意思是public class SubFoo : IFoo { .. }
  • 实际上,在接口中包含默认参数声明对我来说似乎很愚蠢。默认参数应该是实现的一部分,而不是接口。

标签: c# default-value optional-parameters


【解决方案1】:

如果您希望在编译时指示某个方法正在更改可选参数的默认值,您将需要使用某种第 3 方代码分析工具,因为 C# 本身不提供任何提供此类限制的方法,或完成后的任何警告。


作为一种解决方法,一种选择是避免使用可选参数值,而是使用多个重载。由于您在这里有一个接口,这意味着使用扩展方法,以便在一般情况下仍然定义具有默认值的重载实现:

public interface IFoo
{
    void SomeMethod(int para);
}

public static class FooExtensions
{
    public static void SomeMethod(this IFoo foo)
    {
        foo.SomeMethod(0);
    }
}

因此,虽然这种方法在技术上仍然允许某人创建名为 SomeMethod 的扩展(或实例)方法并且不接受 int 参数,但这意味着有人真的需要去主动改变“默认值”。它不要求接口的实现来提供默认值,这可能会导致他们无意中提供错误的默认值。

【讨论】:

  • 如果你锁定foo.SomeMethod()到那种程度,你为什么还需要一个默认/任何参数?
  • @PeterM 我不明白你的问题。这 在功能上提供了一个默认值,它只是不使用 C# 函数作为默认参数来实现该目标。最终结果仍然是使用该接口的人不需要提供int 值,并且0 被用作默认值。它还确保那些实现该方法的人必须积极主动地改变默认值,即使它不能完全阻止它。
  • 我现在可能睡着了,但是如果代码依赖于参数的值一致性,那么我认为该参数是过度规范。
【解决方案2】:

定义const int DefaultPara = 1;,然后使用它代替硬编码数值。

interface IFoo 
{
    void SomeMethod (int para = DefaultPara);
}

public class SubFoo : IFoo {

    public void SomeMethod (int para = DefaultPara) {
        //do something
    }

}

【讨论】:

  • 如果有人仍然使用硬编码的int para = 1怎么办?
【解决方案3】:

没有必要的编译时解决方案 - 但您可以为此进行单元测试(我怀疑您正在认真对待单元测试,如果您提出此类问题,您会经常运行它们)。这个想法是创建像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 中不起作用,因为编译器没有义务发出例如:OptionalBindingFlag。

【讨论】:

    猜你喜欢
    • 2014-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-24
    • 2011-01-14
    • 2019-09-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多