C# 代码契约:如何验证其他程序集中定义的接口方法的参数?

发布于 2024-10-17 08:19:24 字数 874 浏览 1 评论 0原文

我遇到了一个情况,我不知道该如何解决。 根据用户手册第3节,一个contractmethod,即<在重写方法/属性或接口实现中不允许使用 code>Require 或 Ensure。契约方法应在根虚拟/抽象方法中声明,并且由于您无法在抽象方法中声明代码,因此必须使用 ContractClassAttributeContractClassForAttribute。接口成员的契约方法(及其实现)也是如此。

但是如果我想使用一个不是我自己创建的界面怎么办?例如,IList 没有实现这些约定方法,但我也无法在其上设置 ContractClassAttribute。我应该如何在 IList.this[int] 的实现中进行参数验证?不允许使用以下解决方案:

T IList<T>.this[int i]
{
    get
    {
        Contract.Requires(i >= 0);//not allowed

        if (i < 0)
            throw new ArgumentException();
        Contract.EndContractBlock();//also not allowed
        ...
    }
}

不带 EndContractBlock 的旧版 if-then-throw 语句是唯一的解决方案吗?

I have a situation I don't know how it's supposed to be solved.
According to the user manual section 3, a contractmethod, i.e. Require or Ensure, is not allowed in overriding methods/properties or interface implementations. The contract methods should be declared in the root virtual/abstract method and since you can't declare code in an abstract method, you have to work with the ContractClassAttribute and ContractClassForAttribute. The same goes for contractmethods for interfacemembers(and their implementations).

But what if I want to use an interface I haven't created myself? For example IList<T> doesn't implement these contract methods, but I can't set the ContractClassAttribute on it either. How should I do parametervalidation in for example the implementation of IList<T>.this[int]? The following solutions aren't allowed:

T IList<T>.this[int i]
{
    get
    {
        Contract.Requires(i >= 0);//not allowed

        if (i < 0)
            throw new ArgumentException();
        Contract.EndContractBlock();//also not allowed
        ...
    }
}

Are the legacy if-then-throw statements without EndContractBlock the only solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

风苍溪 2024-10-24 08:19:24

首先,IList已经指定了该前提条件。我只是尝试了一下以确保:“前提条件失败:索引 >= 0”。

此外,据我了解,后置条件是允许的,只有先决条件不允许。

如果您还想添加其他先决条件,我相信这可以回答您的问题:
http://social.msdn.microsoft .com/Forums/en/codecontracts/thread/af403bbc-ca4e-4546-8b7a-3fb3dba4bb4a

这基本上可以归结为这样一个事实:您不应该这样做,因为这会违背代码契约的目的。

First of all, IList<T> already specifies that precondition. I just tried it to be sure: "Precondition failed: index >= 0".

Furthermore, as I understand it, post conditions are allowed, only preconditions aren't.

If, you would still like to add other preconditions, I believe this answers your question:
http://social.msdn.microsoft.com/Forums/en/codecontracts/thread/af403bbc-ca4e-4546-8b7a-3fb3dba4bb4a

It basicly comes down to the fact that you are not meant to do so, as that would defy the purpose of code contracts.

月下客 2024-10-24 08:19:24

不能添加前提条件的原因与不能这样做的原因相同:

interface IInterface
{
    void Foo(Parent x);
}

class Implementation : IInterface
{
    void Foo(Child x);
}

它与(同/反)方差相关 - 方法参数是逆变的,这意味着您接受的类型必须“至少与自由主义”作为接口指定的类型。

添加更多先决条件会使类型更小/更严格,就像接受该类型的子类一样(如上面的示例)。*出于明显的原因,您不能这样做 - 一个实现了当传递正确的参数时,接口可能无法工作!如果你翻转了 Parent & 孩子 没关系。

对于方法返回类型,情况正好相反:它们是协变的,这意味着您必须“至少与接口指定的类型一样小/严格”。这就是为什么您可以添加后置条件,因为您正在使类型“更小/更严格”。这与它起作用的原因相同:

interface IInterface
{
    Parent Foo();
}

class Implementation : IInterface
{
    Child Foo();
}

*(这实际上提出了一个问题:您是否应该能够从实现类中删除前置条件。这将是一个有趣的功能。)

The reason you can't add preconditions is the same reason you can't do this:

interface IInterface
{
    void Foo(Parent x);
}

class Implementation : IInterface
{
    void Foo(Child x);
}

It's related to (co-/contra-)variance — method parameters are contravariant, meaning the type you accept has to be "at least as big/liberal" as the type that the interface specifies.

Adding more pre-conditions is making the type smaller/stricter, in the same way that accepting a subclass of the type would be (like in the example above).* You can't do this for obvious reasons — a class which implements the interface might not work when passed the correct arguments! If you flipped Parent & Child it would be ok.

With method return types it's the other way around: they're covariant, meaning you have to be "at least as small/strict" as the type the interface specifies. This is why you can add post-conditions, because you're making the type "smaller/stricter". It's the same reason why this works:

interface IInterface
{
    Parent Foo();
}

class Implementation : IInterface
{
    Child Foo();
}

* (This actually raises the question of whether you should be able to remove pre-conditions from implementing classes. This would be an interesting feature.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文