真的想在C#中使用CodeContracts

我终于开始追赶已经join到.NET 3.5 / 4.0框架中的所有新function。 最近几天我一直在与CodeContracts合作,我真的很想去喜欢它们。 我很好奇别人怎么看待C#中CodeContracts的实现? 具体来说,人们如何组织合同类的接口,合同不变约等方法?

我喜欢合同提供的validation,乍一看,他们看起来不错。 通过几行简单的代码,我甚至可以运行我的代码之前就可以获得一些很好的构build检查。 不幸的是,我很难理解代码合同在C#中的实现方式,他们比编写合同更加混乱了我的代码。 为了充分利用契约,我用假设和断言等方式乱抛我的代码(我知道有些人会说这是件好事)。 但是正如下面的一些例子所显示的那样,它将一条简单的线条变成了四条或五条线,并且在我看来,它并没有为替代方法(即断言,exception等)增加足够的价值。

目前,我最大的挫折是:

界面合同:

[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); } } 

这对我来说就像是一个混乱,我希望有一个更清晰的方式来logging要求,无论是通过属性或某种forms的内置语言支持。 事实上,我必须实现一个实现我的接口的抽象类,以便我可以指定合约似乎充其量是乏味的。

代码Bloat:

 typeof(Action<>).MakeGenericType(typeof(Object); 

需要几个假设来validation可用的信息。 我知道所有的分析器都知道它是在Type上运行的,因此必须在这个有限的知识上工作,但是仍然让我感到沮丧的是,一行代码要求我重写为

 var genericAction = typeof(Action<>); Contract.Assume(genericAction.IsGenericType); Contract.Assume(genericAction.GetGenericArguments().Length == 1); genericAction.MakeGenericType(typeof(Object)); 

只是为了保持logging(是的,我知道我可以使用ContractVerificationAttribute来closures一个方法/类等,或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可以certificate:

 [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(_obj != null); } 

还有更多,但我想我可能已经足够了,而且我真的很感激那些比我更聪明的人的洞察力,来帮助我像“代码合同”那样让代码混乱的感觉消失。 任何有关如何更好地构造代码,解决schemewonkiness问题等方面的见解将不胜感激。

谢谢!

这对我来说就像是一个混乱,我希望有一个更清晰的方式来logging要求,无论是通过属性或某种forms的内置语言支持。

CC团队已经声明使用Attributes不够强大,因为你不能在其中包含lambdaexpression式。 他们可能包括[NotNull]类的东西,但select不这样做,因为他们试图保持CC尽可能通用。

CC是一个库(而不是扩展的C#的一部分)的一个原因是它支持所有的 .NET语言。

你可以在这里阅读更多关于球队的推理。

就实际使用而言,到目前为止,我只是将我的接口契约保存在与接口相同的文件中,这意味着它们都logging在同一个地方。 这是应该改善:)

代码Bloat […]

您的第二个投诉可能是可以实施的东西 – 我会build议张贴在代码合同论坛 。 (编辑:似乎已经有人,但没有答案呢。)

但是,规定不明的合同将需要更多的假设。 如果在.NET框架中遇到类似这样的情况,则可以请求将合同添加到Missing Contracts on Libraries线程中 。

另外,以一个像[…]

这已经解决了 。 如果你有一个自动属性,你只需要添加一个非空的不variables和前/后条件将被生成:

 public class MyClass : IInterface { private Object Property { get; set; } [ContractInvariantMethod] private void Invariants() { Contract.Invariant(Property != null); } } 

无论如何,你最终可能会得到其他不变的东西,所以这不是什么大不了的事情。

是的,合同混乱了事情,但我觉得这是值得PEX读取代码合同,并为我产生25个unit testing和100%的代码覆盖。 如果您还没有使用PEX,那么您错过了代码合同的最大好处。 以下是使用PEX合同的入门指南。

我真的很想喜欢它,并且在一个新项目中实施了很长的一段时间,直到我放弃了我在这个过程中所犯的一些小问题。

我希望微软能够复活Spec#。 我知道代码合约是作为一个库来编写的,可以跨语言使用,但即使只是静态分析的速度,也要求编译器支持。

这家伙提出了一些相同的观点: http : //earthli.com/news/view_article.php?id=2183

无论如何,没有任何一个问题是一个破坏交易的问题,但却是一堆烦恼:

  • 静态分析非常缓慢。 您可以将其设置为在后台运行,但是很容易忽略。
  • 静态分析不是很好,例如:
    • 它只处理简单的控制stream程
    • 它不会假定只读字段是不变的
    • 集合不是由静态检查器处理的(只有运行时)
  • 没有委托合同
  • 除非您主动抑制resharper警告,否则与Resharper不一致
    • Resharper乐观。 它会假设一个方法参数不是null,直到你给它一个暗示它可能是 – 像Contract.Assume(thing != null)
  • 可以产生很多警告和build议,有时源于一个模糊的来源。
    • 当任何一个团队成员放松注意力的时候,警告的数量就会变得压倒人心
  • 为接口指定抽象合同类是很痛苦的。
  • 运行时合同使用IL重写,这显然不能很好地与其他重写解决scheme,如PostSharp(我想。
    • 由于IL重写,编辑和继续不能使用
  • 契约对利斯科夫替代原则坚持不懈:亚型永远不能放松先决条件。 我的意思是说,原则上是好的,但是我想在处理签约的第三方代码的时候会很痛苦。

我已经为Visual Studio 2010创build了一个插件,它可以节省您一些时间为接口和抽象类创build代码合同: http : //visualstudiogallery.msdn.microsoft.com/en-us/d491911d-97f3-4cf6-87b0-6a2882120acf

其实,我觉得界面的实现很棒。 这不是混淆你的界面或你的常规代码,你可以整洁地存储在你的解决scheme的某个文件夹中,或者与你的界面相同的类文件中,无论你最喜欢哪一个。

为什么你需要添加对象不variables? 你只需要添加尽可能多的,你觉得有必要。

我理解你的代码膨胀的观点,但我想这是代码合同的折衷。 额外的检查/安全意味着额外的代码,至less目前如何实现。 我会尽量保持尽可能多的契约,至less应该帮助你缓解代码膨胀的痛苦。

如果你担心代码膨胀,那么我会鼓励你去看看Aspect Oriented Programming作为替代scheme。

这将允许您在该方面指定合同,然后在合同中包装任何类或方法的类。 如果你的合同将被用于多个class级,那么这将是一个更清晰的方式来实现这一点。

不幸的是,在C#中对AOP的支持并不好,但是有几个库增加了这个function。

而且我也对C#的其他实现有同样的感觉,就像你在Contracts中find的一样,一开始看起来都很好,直到你拿起引擎盖并开始严肃地使用它。