非空接口代码契约的实现 - 默认(T)与抛出NotImplementedException

时间:2014-03-07 00:48:11

标签: c# code-contracts

这可能主要是样式问题,但在为非void接口成员定义代码契约时,哪种方法最好:

接口:

[ContractClass(typeof(IFooContract))]
public interface IFoo
{
    object Bar();
}

合同选项1:

[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
    object IFoo.Bar()
    {
        Contract.Ensures(Contract.Result<object>() != null);
        throw new NotImplementedException();
    }
}

合同选项2:

[ContractClassFor(typeof(IFoo))]
public abstract class IFooContract : IFoo
{
    object IFoo.Bar()
    {
        Contract.Ensures(Contract.Result<object>() != null);
        return default(T);
    }
}

我见过的大多数文献倾向于选项2,但我觉得选项1更好,因为更清楚的是这纯粹是关于合同(而选项2在技术上违反了它刚刚定义的合同)。

是否存在选项2优先于选项1的情况?

2 个答案:

答案 0 :(得分:2)

抛出异常在语义上更正确,因为无法再以一种看似合理的方式调用和使用契约类;呼叫者将被停止并告知他们的错误。

但是,NotImplementedException似乎不是抛出的正当例外:该异常通常标记尚未实现的代码段(例如方法体)。但是已经实施了非void合同方法 ;它只是不打算被称为。因此,我更喜欢InvalidOperationExceptionNotSupportedException

(您甚至可以抛出自定义异常类型,例如NotMeantToBeCalledException。)

答案 1 :(得分:1)

这应该是一个完全主观(和学术)的论点,因为永远不应该调用ContractClassFor类中的代码。 :-)。正如您所暗示的那样(通过non-void),exceptiondefault路线只需要在课程中进行编译,并且与此处包含的合同无关。方法

显然,抽象类无法直接实例化,但是,您应该将ContractClassFor类的可见性降低到internal,以减少意外子类化的变化。

此外,Jon Skeet更进一步,并添加private parameterless constructor以防止任何实例化的可能性:

private IFooContract() { }

(并间接回答你的问题,进一步表达这是一个特殊的&#34;合同助手类这样一个事实,即合同无法直接放在界面中,您的代码的读者将很容易识别它,只需忽略default / exception编译器安慰剂)