【问题标题】:Restrict value of a parameter in a constructor AT DESIGN TIME在设计时限制构造函数中参数的值
【发布时间】:2010-07-24 22:37:07
【问题描述】:

我想将构造函数中数字参数的值限制在一定范围内。

我知道常规方法是执行以下操作:

public class Foo
{
    public int MaxAmount { get; }
    public int Amount { get; set; }

    public Foo(int amount)
    {
        if (amount > MaxAmount) { Amount = MaxAmount; }
        else if (amount < 1) { Amount = 1; }
        else { Amount = amount; }
    }
}

但我不喜欢的是调用者不知道何时将属性设置为指定以外的值。我可以返回一个异常而不是静默地限制值,但这不是很友好。

我想要的是类似于这样的东西:

public Foo(int(1, this.MaxAmount) amount) // Where int(minimumValue, maximumValue)
{
   Amount = amount;
}

其中一个甚至无法用不可接受的值实例化 Foo - 框架会阻止它。

这样的事情可能吗?

为清晰而编辑:

我所追求的是参数本身可以携带和传达有关其约束的信息的一种方式 - 以“烘焙”方式,例如,在 Intellisense 中,当你写了电话。因此,如果参数的值无效,我什至会避免尝试实例化类的工作。

例如,如果程序正在运行并且用户键入一个数字 (N) 并按下一个按钮,该按钮创建了一个非法数量 N 的新 Foo,我现在有一个异常需要处理,并且有一些东西需要调试和修复.为什么甚至首先允许它?如果 Foo 已明确定义为其 Amount 属性的上限为 4,那么当我本可以通知他他传递的值在他传递的时候无效时,允许开发人员编写 Foo(5) 的意义何在?写的?

如果有一些语法糖,例如 ParameterConstraint 或其他东西,由框架为我处理,这样我就不必在我编写的每个类中都添加自己的语法糖,我认为这将非常有用。

【问题讨论】:

    标签: c# .net parameters constructor


    【解决方案1】:

    我可以返回一个异常而不是静默限制值,但这不是很友好。

    说什么? “友好”是什么意思?调用者不是你的朋友,它是另一段试图设置超出范围值的代码。应该立即告诉编写代码的开发人员他做错了什么。

    抛出异常!

    【讨论】:

    • 我想通知调用者该值无效,但不做尝试实例化类的工作。我认为让 Intellisense 响应它是无效的会很好。
    【解决方案2】:

    您可以通过 Code Contracts 使用静态合同检查来执行此操作(仅限高级版 - 标准版仅提供运行时合同检查)。

    语法很简单

    public Foo(int amount) {
        Contract.Requires(amount < MaxAmount);
        ...
     }
    

    (需要)通过在调用方法时检查参数是否受到约束来评估合同。在您的实例中,很难针对实例字段 MaxAmount 评估构造函数参数,因为您无法事先检查该值。 (将 MaxValue 设为静态以解决此问题)。

    此类调用的示例。

    int val = _getFromSomewhere();
    var foo = new Foo(val); 
    //This May produce compile time error 
    // because the contract checker cannot prove you contract is met.
    

    解决方法是确保将约束放在调用的位置。

    int val = _getFromSomewhere();
    if (val < Foo.MaxAmount)
        var foo = new Foo(val); 
        //Will always compile fine, because contract is met.
    

    安装合同时,静态检查器默认不会打开。您的项目属性将有一个额外的选项卡,您可以在其中配置合同选项并启用静态检查。

    【讨论】:

    • 请注意,使用Contract.Requires 是不够的。你仍然需要if (...) throw new ArgumentOutOfRangeException()
    【解决方案3】:

    然后,您需要将参数包装在一个新类型中。 int 知道他们的最大数量是多少,它是 int.MaxValue。如果参数本身确实知道它自己的最大数量,并且它不是特定于类 Foo 的东西,那么您将需要创建另一种类型来检查传递给它的数量。就目前而言,Foo 的构造函数的签名接受任何 int 数据结构。

    【讨论】:

      【解决方案4】:

      要么抛出异常,要么提供静态属性来验证金额

      public static bool ValidateAmount(int amount)
      {
          if(amount > MaxAmount)
              return false;
          return true;
      }
      

      【讨论】:

        【解决方案5】:

        不确定这是否适用于 C# 中的属性类型,但您可以定义一个包含所有可接受值的枚举,然后将属性的数据类型设置为该枚举。这将迫使调用者使用枚举,从而知道哪些值是可以接受的。当然,如果你有很多值在可接受的范围内,枚举就会很笨拙。

        【讨论】:

          猜你喜欢
          • 2013-03-04
          • 2017-08-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-14
          • 1970-01-01
          相关资源
          最近更新 更多