我终于赶上了 .NET 3.5/4.0 框架中添加的所有新内容。最近几天我一直在使用 CodeContracts,我真的很努力地喜欢它们。我很好奇其他人对 C# 中 CodeContracts 的实现有何看法?具体来说,人们如何组织诸如接口的契约类、契约不变量的契约方法等?
我喜欢合同提供的验证,乍一看它们看起来很棒。只需几行简单的代码,我什至可以在运行代码之前进行一些不错的构建检查。不幸的是,我很难克服在 C# 中实现代码契约的方式的感觉,它们使我的代码更加混乱,而不是记录契约。为了充分利用合同,我在我的代码中乱扔假设和断言等(我知道有些人会说这是一件好事);但正如我下面的一些示例将显示的那样,它将单个简单的行变成 4 或 5 行,并且在我看来并没有真正增加替代方法(即断言、异常等)的足够价值。
目前,我最大的挫败感是:
接口合约:
[ContractClass(typeof(IInterfaceContract))]
public interface IInterface
{
Object Method(Object arg);
}
[ContractClassFor(typeof(IInterface))]
internal abstract class IInterfaceContract
{
private IInterfaceContract() { }
Object IInterface.Method(Object arg)
{
Contract.Requires(arg != null);
Contract.Ensures(Contract.Result<Object>() != null);
return default(Object);
}
}
这对我来说就像是一个障碍,我希望有一种更简洁的方式来记录需求,无论是通过属性还是某种形式的内置语言支持。我必须实现一个抽象类来实现我的接口,这样我才能指定合同,这一事实充其量似乎很乏味。
代码膨胀:
typeof(Action<>).MakeGenericType(typeof(Object);
需要几个假设来验证现成的信息。我很感激分析器所知道的只是它在 Type 上运行,因此必须使用有限的知识,但仍然让我感到沮丧的是,一行代码需要我重写为
var genericAction = typeof(Action<>);
Contract.Assume(genericAction.IsGenericType);
Contract.Assume(genericAction.GetGenericArguments().Length == 1);
genericAction.MakeGenericType(typeof(Object));
只是为了将事情记录在案(是的,我知道我可以使用 ContractVerificationAttribute 为方法/类等关闭此功能,或使用 SuppressMessageAttribbute 来针对特定消息,但这似乎违背了目的,因为您的代码很快就会充满抑制等。
另外,举个例子
public class MyClass
: IInterface
{
private readonly Object _obj;
public Object Property
{
get
{
Contract.Ensures(Contract.Result<Object>() != null);
return _obj;
}
}
public MyClass(Object obj)
{
Contract.Requires(obj != null);
_obj = obj;
}
}
obj 必须不为 null,并且设置为无法更改的只读字段,但我仍然需要向我的类添加“标记”方法,以便可以证明我的属性要求不返回 null:
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(_obj != null);
}
还有更多,但我想我可能已经咆哮得够多了,我真的很感谢比我聪明得多的人的洞察力来帮助我“喜欢”代码合同并消除这种代码混乱的感觉。任何关于如何更好地构建代码、解决不稳定问题等的见解将不胜感激。
谢谢!