我终于在.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);
}
}
Run Code Online (Sandbox Code Playgroud)
这感觉就像这样一个cludge对我来说,我希望有一个更清洁的记录要求的方式,无论是通过属性或某种形式的内置的语言支持.我有这样的事实来实现,实现我的接口一个抽象类,只是让我可以指定合约似乎有些单调乏味的最好的.
代码膨胀:
typeof(Action<>).MakeGenericType(typeof(Object);
Run Code Online (Sandbox Code Playgroud)
需要几个假设才能验证现有的信息.我明白,所有的分析都知道的是,它是在型操作,因此必须在有限的知识工作,但它仍然让我很沮丧的是一个单一的代码行要求我重新写为
var genericAction = typeof(Action<>);
Contract.Assume(genericAction.IsGenericType);
Contract.Assume(genericAction.GetGenericArguments().Length == 1);
genericAction.MakeGenericType(typeof(Object));
Run Code Online (Sandbox Code Playgroud)
只是为了记录事情(是的,我知道我可以使用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) …Run Code Online (Sandbox Code Playgroud) 有没有办法向ReSharper表明由于按合同设计需要检查,不会出现空引用?例如,以下代码将Possible 'null' assignment to entity marked with 'NotNull' attribute在第7行和第8行的ReSharper中引发warning():
private Dictionary<string, string> _Lookup = new Dictionary<string, string>();
public void Foo(string s)
{
Contract.Requires(!String.IsNullOrEmpty(s));
if (_Lookup.ContainsKey(s))
_Lookup.Remove(s);
}
Run Code Online (Sandbox Code Playgroud)
真正奇怪的是,如果删除该Contract.Requires(...)行,ReSharper消息就会消失.
更新
我通过ExternalAnnotations找到了解决方案,Mike也在下面提到过.以下是如何在Microsoft.Contracts中为函数执行此操作的示例:
Microsoft.Contracts在ExternalAnnotationsReSharper目录下调用的目录.Microsoft.Contracts.xml并填充如下:<assembly name="Microsoft.Contracts">
<member name="M:System.Diagnostics.Contracts.Contract.Requires(System.Boolean)">
<attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
<parameter name="condition">
<attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
<argument>0</argument>
</attribute>
</parameter>
</member>
</assembly>
Run Code Online (Sandbox Code Playgroud)
c# resharper design-by-contract code-contracts microsoft-contracts
我刚刚安装了新发布的Visual Studio 2017 Enterprise(RC).但是我无法使用Microsoft CodeContracts.我对使用VS2015的CodeContract没有任何问题.我错过了什么吗?
在我的测试项目中无法访问System.Diagnostics.Contracts.ContractException.请注意,这段代码纯粹是我自己搞乱了我的Visual Studio新副本,但我想知道我做错了什么.
我正在使用VS的专业版,因此我没有静态检查.为了仍然使用代码契约(我喜欢),我认为我的方法可以工作的唯一方法是捕获在运行时抛出的异常,但我发现这不可能.
测试方法
[TestMethod, ExpectedException(typeof(System.Diagnostics.Contracts.ContractException))]
public void returning_a_value_less_than_one_throws_exception()
{
var person = new Person();
person.Number();
}
Run Code Online (Sandbox Code Playgroud)
方法
public int Number()
{
Contract.Ensures(Contract.Result<int>() >= 0);
return -1;
}
Run Code Online (Sandbox Code Playgroud)
错误
Error 1 'System.Diagnostics.Contracts.ContractException' is inaccessible due to its protection level.
编辑
经过一番思考后,我得出了评论中讨论的结论,以及以下内容.给定一种方法,如果这有一个可以用代码合同形式表达的要求,我会写这样的测试.
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void value_input_must_be_greater_than_zero()
{
// Arrange
var person = new Person();
// Act
person.Number(-1);
}
Run Code Online (Sandbox Code Playgroud)
这将确保合同是代码的一部分,并且不会被删除.这将要求Code Contract实际抛出指定的异常.但在某些情况下,不需要这样做.
微软最近在DevLabs上发布了他们的Code Contracts框架并获得了商业许可.我们有兴趣在我们的项目中使用它们(主要是C#,一些C++/CLI)来逐步替换所有自定义验证代码,但我很想知道其他人在我们承诺之前使用它的经验,特别:
您是否认为该框架对于大型复杂的商业项目而言已经足够成熟?
使用它时遇到了什么问题?
你从中得到了什么好处?
它目前是否比它的价值更痛苦?
我意识到这是一个有点主观的问题,因为它需要意见,但鉴于这个框架是.NET 4.0的一个非常重要的部分,并且(可能)改变了我们编写验证代码的方式,我希望这个问题将会留下开放以收集有关该主题的经验,以帮助我做出具体的,可回答的问题的决定:
我们下个月应该开始使用吗?
请注意,我们不提供代码API,只提供Web服务,因此对于抛出异常类型方面的大多数代码打破兼容性并不是一个问题.然而,正如我希望更多人而不仅仅是我将受益于这篇文章及其答案,这个领域的任何细节都非常受欢迎.
我什么时候应该调试代码合同上的问题,反之亦然?我想检查一个方法的前提条件,我很困惑,选择一个而不是另一个.我有单元测试,我想测试故障情况并期望异常.
在同一方法上使用Debug.Assert和Code契约是一个好习惯.如果是这样,代码的编写顺序是什么?
Debug.Assert(parameter!= null);
Contract.Requires<ArgumentNullException>(parameter != null, "parameter");
Run Code Online (Sandbox Code Playgroud)
要么
Contract.Requires<ArgumentNullException>(parameter != null, "parameter");
Debug.Assert(parameter!= null);
Run Code Online (Sandbox Code Playgroud)
它背后有什么理由吗?
例如,本文介绍了它们.
有什么好处?
静态分析似乎很酷,但同时它会阻止在单元测试中传递null作为参数的能力.(如果您按照文章中的示例进行操作)
关于单元测试的主题 - 如果您已经实施自动化测试,那么现在肯定没有任何关系可以代码合同吗?
更新
玩过代码合约后,我有点失望.例如,根据接受的答案中的代码:
public double CalculateTotal(Order order)
{
Contract.Requires(order != null);
Contract.Ensures(Contract.Result<double>() >= 0);
return 2.0;
}
Run Code Online (Sandbox Code Playgroud)
对于单元测试,您仍然必须编写测试以确保无法传递null,并且如果契约是业务逻辑,则结果大于或等于零.换句话说,如果我要删除第一个合同,除非我特意测试了这个功能,否则没有测试会中断.这是基于不使用内置于Visual Studio的更好(终极等)版本中的静态分析.
请参阅下文,了解正在进行的操作
我有一个非常奇怪的问题,捕获的异常是null.
该代码使用MEF并努力报告组合错误.使用调试器我可以看到抛出异常(a InvalidOperationException)但是当它被下面代码中的最后一个catch块捕获时,ex变量为null.在调试器和正常执行代码时都是如此.
static T ResolveWithErrorHandling<T>() where T : class
{
try
{
IocContainer.Compose(Settings.Default.IocConfiguration);
return IocContainer.Resolve<T>();
}
catch (ReflectionTypeLoadException ex)
{
// ... special error reporting for ReflectionTypeLoadException
}
catch (Exception ex)
{
// ex is null - that should not be possible!
// ... general error reporting for other exception types
}
return null;
}
Run Code Online (Sandbox Code Playgroud)
我用注释替换的代码是格式化错误消息的非常简单的代码.那里没什么奇怪的.
我试图改变代码以发现可能产生的影响:
ReflectionTypeLoadException),则最终catch块中捕获的异常不再为null.InvalidOperationException为第一个catch块添加一个catch块,则该块中捕获的异常不为null.InvalidOperationException在两个catch块之间添加一个catch块,则该块中捕获的异常为null.该项目使用代码契约,并对编译器生成的代码进行后处理以检查合同.不幸的是,我没有找到一种方法来摆脱测试目的,而不对项目进行大手术.
我目前的解决方法是不捕获ReflectionTypeLoadException并改为分支ex一般异常处理程序中的类型. …
一旦警告级别达到第二级或更高级别,为什么我会在这个简单的代码示例中收到以下警告?
public int Foo(int a)
{
if (a >= 0) throw new ArgumentException("a should be negative", "a");
Contract.EndContractBlock();
return a;
}
Run Code Online (Sandbox Code Playgroud)
CodeContracts:建议要求:此前提条件是多余的:请考虑将其删除.您是否将结构值与null进行比较?
显然,整数可以是负数,因此前提条件几乎不是多余的,为什么我会收到此警告?
编辑:这是ILSpy在查看exe时为创建的函数显示的内容:
public int Foo(int a)
{
if (a >= 0)
{
ContractHelper.RaiseContractFailedEvent(ContractFailureKind.Precondition, null, "a < 0", null);
throw new ArgumentException("a should be negative", "a");
}
return a;
}
Run Code Online (Sandbox Code Playgroud)

我们正在迁移到.NET 4,并且对实现新的按设计合同功能非常感兴趣.
我们知道Code Contract引擎需要安装Code Contract addin

和VS Ultimate或Premium(用于静态检查).
这是我的问题:
code-contracts ×10
c# ×7
.net-4.0 ×3
.net ×2
assert ×1
mef ×1
resharper ×1
teamcity ×1
unit-testing ×1
validation ×1