【问题标题】:Should conditions be repeated when referencing another contracted method?引用另一种约定方法时是否应重复条件?
【发布时间】:2012-02-13 14:57:23
【问题描述】:

以下是接口契约的摘录。

我的问题是:前一种方法是否应该重复后一种方法的前提条件?由于合同是公开的,是否可以省略重复?在这种情况下,它只是一个非空检查,但我可以想象在运行时重复相同的检查时重复大量代码并且性能受到影响的情况。

public int CommandConsumerCount(IWriteCommand writeCommand)
{
    Contract.Requires(writeCommand != null); // redundant?
    Contract.Requires(this.IsOwnerOf(writeCommand));

    Contract.Ensures(Contract.Result<int>() >= 0);

    return default(int);
}

public bool IsOwnerOf(IWriteCommand writeCommand)
{
    Contract.Requires(writeCommand != null);

    return default(bool);
}

【问题讨论】:

    标签: .net c#-4.0 code-contracts


    【解决方案1】:

    这取决于该条件(在本例中为writeCommand != null)是 CommandConsumerCount 方法和 IsOwnerOf 方法的要求,还是仅 IsOwnerOf 方法的要求。

    如果只有 IsOwnerOf 方法真正需要该条件,则可以从 CommandConsumerCount 方法中省略它。

    但是,如果这两种方法都需要该条件,那么我将坚持我的原始答案:

    我认为由于您的两种方法都是公开的,因此应该重复合同要求。如果您有一个执行实际工作的私有方法,IsOwnerOf 方法和 CommandConsumerCount 方法都调用了该方法(而不是调用 IsOwnerOf 方法的 CommandConsumerCount 方法),那么在其中省略对 Contract.Requires 的调用就可以了私有方法。

    就性能而言...我不会担心这些检查对性能的影响,除非检查本身的逻辑非常复杂。您可以设置编译器以从项目属性的“代码合同”部分的编译输出中排除对 Contract.Requires 的调用(假设您已安装必要的插件)。

    不过,也许我错过了你的问题的重点。您是在问是否可以在 CommandConsumerCount 方法中完全省略对 IsOwnerOf 的调用?在这种情况下,我会保留调用,但如果这是一个性能问题,那么我会配置项目以排除对 Release 版本的调用,假设我已经使用 Debug 版本进行了足够的测试以确保这种情况是满意。

    编辑:重新阅读问题后,您似乎很清楚您在询问writeCommand != null 检查,所以我删除了上面的段落。

    下面的代码示例添加了一个执行 IsOwnerOf 方法实际工作的私有方法。

        // you may want to choose a different name for this method
        private bool _IsOwnerOf(IWriteCommand)
        {
            // actual work is done here in this private method
            return default(bool);
        }
    
        public bool IsOwnerOf(IWriteCommand writeCommand)
        {
            Contract.Requires(writeCommand != null);
    
            // call the private method to perform the actual work
            return _IsOwnerOf(writeCommand);
        }
    
        public int CommandConsumerCount(IWriteCommand writeCommand)
        {
            Contract.Requires(writeCommand != null);
            Contract.Requires(_IsOwnerOf(writeCommand)); // call the private _IsOwnerOf method instead of the public method
    
            Contract.Ensures(Contract.Result<int>() >= 0);
    
            return default(int);
        }
    

    【讨论】:

      【解决方案2】:

      一般来说,如果方法 A 调用方法 B,那么 A 必须确保 B 上的所有合约都为真。

      但是,如果在 Requires 中使用该方法,则它是多余的,因为所有 Requires 都必须由方法的调用者证明。

      例如,如果您有:

      CommandConsumerCount(x);
      

      如果没有关于x 的信息,那么静态检查器会抱怨IsOwnerOf 必须为真。为了证明IsOwnerOf,您的代码必须执行以下操作:

      if (IsOwnerOf(x))
      {
          CommandConsumerCount(x);
      }
      

      或:

      IWriteCommand GetWriteCommand()
      {
          Contract.Ensures(IsOwnerOf(Contract.Result<IWriteCommand>()));
      
          //...
      }
      
      var x = GetWriteCommand();
      CommandConsumerCount(x);
      

      在这两种情况下,IsOwnerOf 非空合约也将被检查,因此证明该参数不为空。

      但是,如果您有 CommandConsumerCount 改为这样:

      int CommandConsumerCount(IWriteCommand command)
      {
          Contract.Requires(command != null);
      
          if (IsOwnerOf(command))
          {
             // ...
          }
      
          return 0;
      }
      

      在这种情况下,CommandConsumerCount 需要合约,因为调用者没有义务证明 IsOwnerOf 为真,因此不会检查非空合约。

      【讨论】:

      • 关于静态检查器的非常酷的信息。我自己刚刚开始使用合同。有趣的是如何使用一个方法的 Ensures 条件来满足随后调用的方法的 Requires 条件,例如在 GetWriteCommand 代码示例中。
      【解决方案3】:

      我会说每个方法都应该只指定它需要的条件,并且不知道它调用的条件方法。

      所以如果CommandConsumerCount 需要writeCommand 不为空,那么拥有合约并不是多余的。

      但是,如果CommandConsumerCount 需要writeCommand 不为空的唯一原因是将其传递给IsOwnerOf,那么它是多余的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-17
        • 2018-03-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多