我一直在使用新的 System.Diagnostics.Contracts 类,因为它一开始看起来非常有用。用于检查入站参数、返回值等的静态方法。它是一个干净的接口,可以替换大量 if-then 语句和内部构建的库工具。
然而,它在大多数运行时情况下似乎不太有用。据我所知,它不会引发错误,因此我无法捕获任何信息来了解合同是否失败。它会弹出一个包含错误的对话框。如果我在很少有人查看的远程机器上运行 wcf 服务...我怎么知道合约失败了?如果我不能捕捉到错误发生的事实,我怎么能让服务的调用者知道他们出了问题?
Throw-Catch 已经存在了一段时间,我不明白为什么 Contracts 要绕过它。我是否试图错误地使用这个东西?如果是这样,那么有人给我一个现实生活中的情况,其中运行时合同是有意义的。
肯
I've been playing with the new System.Diagnostics.Contracts class because it seemed very useful at first. Static methods to check inbound arguments, return values, etc. It was a clean interface and could replace lots of if-then statements and internally built library tools.
However, it seems less than useful in most runtime situations. From what I can tell, it doesn't throw an error so I can't catch anything to know if a contract failed. It pops up a dialog box with the error. If I'm running a wcf service on a remote box that rarely has a human look at it...how would I know the contract failed? If I can't trap the fact that the error happened, how could I let the caller of the service know they f'd up?
Throw-Catch has been around for awhile, I don't understand why Contracts want to bypass this. Am I trying to use this thing incorrectly? If so, then someone give me a real-life situation where a runtime Contract makes sense.
Ken
发布评论
评论(1)
如果您需要抛出特定异常以调用要捕获的代码,则应使用
Contract.Requires
方法或旧版 if-then-throws,后跟Contract.EndContractBlock()
。例如,当其他代码已经期望并依赖于抛出的常规异常时,您可以执行此操作。请参阅 用户手册,了解何时使用不同形式的前提条件的完整说明。
如果您在项目设置的“代码合同”选项卡中取消选中“合同失败时断言”,则在调试时您将在代码中的违规点处收到实际的异常,而不是对话框。然而,这并不是为了捕捉。
第 7.5 节:运行时行为的合理性 和第 7.6 节:ContractException /C2715F76-F56C-4D37-9231-EF8076B7EC13/userdoc.pdf" rel="noreferrer">用户手册 解释为什么它以这种方式工作。
这个想法是,您永远不需要编写包含处理合同违规的特定逻辑的程序。它们永远不应该出现在正确的程序中,并且表明代码中存在需要修复的严重错误。这与避免捕获
ArgumentNullException
的方式类似。在不表明代码本身存在错误的特殊情况下,例如找不到文件时,您仍然应该抛出常规异常。这允许调用代码适当地处理该情况。
最好在使用软件之前尽可能多地对其进行测试,以确保它不会违反任何合同。
如果您有重写运行时行为的特定需要,您可以通过编写自己的合约运行时类来实现。请参阅第 7.7 节:提供自定义合同运行时类 EF8076B7EC13/userdoc.pdf" rel="noreferrer">用户手册了解更多信息。
编辑:回应下面的评论...
前置条件是定义何时允许调用方法的契约,并且意味着调用者在调用该方法时进行验证。运行时检查器将在调用代码中注入适当的检查,而不是方法本身。如果您有可用的静态检查器,每当您在调用该方法之前无法确保先决条件时,它都会向您指出。
在您的情况下,处理数据的内部方法应该定义前提条件,说明它们允许哪些类型的数据。当数据从你无法控制的外部来源传入时,你应该验证它并以通常的方式处理它;您不想为此使用代码契约。但是,例如,如果您忘记完全验证该数据,则在使用前提条件调用内部代码时将遇到合同违规。这表明您自己的代码中存在错误。
If you need to throw a particular exception for calling code to catch, then you should use the
Contract.Requires<TException>
methods or legacy if-then-throws followed byContract.EndContractBlock()
. You'd do this when other code already expects and depends on regular exceptions being thrown, for example.See Section 5.1: Argument Validation and Contracts of the user manual for a full explanation of when to use the different forms of precondition.
If you untick "Assert on Contract Failure" in the "Code Contracts" tab of your project settings, you will get an actual exception thrown at the offending point in code while debugging, rather than a dialog box. This is not for catching, however.
Section 7.5: Rational for Runtime Behaviour and Section 7.6: ContractException of the user manual explain why it functions this way.
The idea is that you should never need to write programs that contain specific logic that handles contract violations. They should never happen in correct programs and indicate a serious fault in the code that needs to be fixed. This is similar to how you should avoid catching
ArgumentNullException
.You should still throw regular exceptions in exceptional circumstances that do not indicate faults in the code itself, such as when files could not be found. This allows the calling code to handle that circumstance as appropriate.
Preferably, you should test the software as much as you can before putting it to use, to make sure that it never violates any contracts.
If you have a specific need to override the runtime behaviour, you can do so by writing your own contract runtime class. See Section 7.7: Providing a Custom Contract Runtime Class of the user manual for more information.
Edit: In response to the comment below...
Preconditions are contracts that define when a method is allowed to be called, and are meant to be verified by the caller at the time of calling the method. The runtime checker will inject the appropriate checks in the calling code, not the method itself. If you have the static checker available, it will point out to you whenever you fail to ensure the preconditions before calling that method.
In your situation, your internal methods that process data should define preconditions stating what kind of data they allow. When data is passed in from outside sources that you cannot control, you should validate it and handle it in the usual way; you don't want to use code contracts for this. However, if for example you forget to fully validate that data, you will then encounter a contract violation when calling the internal code with preconditions. This indicates a bug in your own code.