为什么我不能在重写方法中添加Contract.Requires?

时间:2011-09-05 21:23:43

标签: c#-4.0 code-contracts

我正在使用代码合同(实际上,使用此方法学习)。

我面临一些奇怪的事情......我重写了一个在第三方程序集中定义的方法。我想添加一个Contract.Require语句,如下所示:

public class MyClass: MyParentClass
{
    protected override void DoIt(MyParameter param)
    {
        Contract.Requires<ArgumentNullException>(param != null);

        this.ExecuteMyTask(param.Something);
    }

    protected void ExecuteMyTask(MyParameter param)
    {
        Contract.Requires<ArgumentNullException>(param != null);
        /* body of the method */
    }
}

但是,我收到这样的警告:

  

警告1 CodeContracts:   方法'MyClass.DoIt(MyParameter)'覆盖'MyParentClass.DoIt(MyParameter))',因此无法添加Requires。

[edit] 稍微更改了代码以显示替代问题 [/ edit]

如果我删除了DoIt方法中的Contract.Requires,我会收到另一个警告,告诉我必须提供未经证实的param != null 我不明白这个警告。原因是什么,我可以解决吗?

2 个答案:

答案 0 :(得分:14)

您无法添加呼叫者可能不知道的额外要求。它违反了Liskov's Subtitution Principle。多态性的一点是,调用者应该能够处理实际引用派生类的实例的引用,就好像它引用了基类的实例一样。

考虑:

MyParentClass foo = GetParentClassFromSomewhere();
DoIt(null);

如果这是静态地确定有效,那么你的派生类举起手并说“不!你不打算用空参数调用DoIt是错误的!”合同的静态分析的目的是你可以在编译时确定调用,逻辑等的有效性......所以在执行时不能添加额外的限制,这是由于多态性而发生的。

派生类可以添加对它将要执行的操作的保证 - 它将确保什么 - 但它不能再为调用者提供需求以用于重写方法。

答案 1 :(得分:1)

我想请注意,您可以执行Jon建议的内容(此答案会在his后添加),但也可以在不违反LSP的情况下签订合同。

您可以将override关键字替换为new

基地仍然是基地;你所做的就是引入另一种功能(正如关键字字面意思所示)。

静态检查并不理想,因为安全性很容易被丢弃(首先转换为基类,然后调用方法)但这是必须的,否则它会违反LSP并且你不想要显然这样做。我会说,总比没有好。

在一个理想的世界中,你也可以覆盖方法并调用新的方法,但是C#不会让你这样做,因为这些方法具有相同的签名(即使它是完全合理的;这是权衡取舍)。