C# 代码契约、空检查和值/引用类型

C# 代码契约、空检查和值/引用类型,c#,null,code-contracts,value-type,reference-type,C#,Null,Code Contracts,Value Type,Reference Type,更新后的帖子:为了避免混淆我在做什么和没有做什么,我对这篇帖子进行了彻底的编辑,以包含导致此问题的完整代码示例。为了使这篇文章可读,所有的代码都张贴在底部 背景: 我正在编写一个流畅的测试界面(我知道已经完成了,但一半的目的是了解它是如何工作的…),我想验证myNumber是否介于3和10之间,代码行如下 myNumber.ShouldBeLessThan(10).And.ShouldBeGreaterThan(10); myListOfCars.ShouldNotBeNull().And.S

更新后的帖子:为了避免混淆我在做什么和没有做什么,我对这篇帖子进行了彻底的编辑,以包含导致此问题的完整代码示例。为了使这篇文章可读,所有的代码都张贴在底部


背景: 我正在编写一个流畅的测试界面(我知道已经完成了,但一半的目的是了解它是如何工作的…),我想验证
myNumber
是否介于3和10之间,代码行如下

myNumber.ShouldBeLessThan(10).And.ShouldBeGreaterThan(10);
myListOfCars.ShouldNotBeNull().And.ShouldBeA<IEnumerable<Car>>();
和helper
类:

public class AndHelper<T>
{
    protected readonly T val;

    public AndHelper(T value)
    {
        this.val = value;
    }

    public virtual T And { get { return this.val; } }
}
公共类和助手
{
受保护的只读T值;
公共和辅助程序(T值)
{
this.val=值;
}
公共虚拟T和{get{返回this.val;}}
}
两个子类,
ReferenceAndHelper

公共类引用和助手:和助手
T:在哪里上课
{
公共引用和帮助程序(T值)
:基准(值)
{
Contract.Requires(值!=null);
}
公共覆盖T和
{
得到
{
Contract.sure(Contract.Result()!=null);
返回val;
}
}
[收缩变量法]
void valueisnotNull不变量()
{
Contract.Invariant(this.val!=null);
}
}
StructAndHelper

公共类StructAndHelper:AndHelper
其中T:struct
{
public StructAndHelper(T值)
:基准(值)
{
}
公共覆盖T和
{
得到
{
返回此.val;
}
}
}

不是创建两个具有不同约束的
和helper
类,而是创建一个
非null和helper
来断言其值不为null的不变量?这将只由能够保证其结果为非null的帮助函数返回,无论是由于需求还是作为其函数的副作用(如IsNotNull)。这将使合同得以证明

代码契约是无法实现的 验证和(上的属性) AndHelper)将永远不会返回null

为什么不呢?除非我误解了你的问题,否则你可以编写如下代码:

public class AndHelper<T>
{
    protected readonly T val;
    public T And { get { return val; } }

    public AndHelper(T value)
    {
        Contract.Requires(value != null);
        val = value; 
    }

    [ContractInvariantMethod]
    void Invariants()
    {
        Contract.Invariant(And != null);
    }
}
公共类和助手
{
受保护的只读T值;
公共T和{get{return val;}}
公共和辅助程序(T值)
{
Contract.Requires(值!=null);
val=值;
}
[收缩变量法]
void不变量()
{
Contract.Invariant(And!=null);
}
}
从那时起,合同检查人员将确保和值永远不会为空


我误解你的问题了吗?

我知道这是一个老问题,但我看不到公认的答案,所以我想试试

不要有两个子类
和helper
,而是将
和helper
更改为以下内容:

public class AndHelper<T>
{
    private readonly T val;

    public AndHelper(T value)
    {
        Contract.Requires(!ReferenceEquals(value, null));

        this.val = value;
    }

    public virtual T And 
    { 
        get 
        { 
            Contract.Ensures(!ReferenceEquals(Contract.Result<T>(), null));

            return this.val; 
        } 
    }

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(!ReferenceEquals(val, null));
    }

}
公共类和助手
{
私有只读T值;
公共和辅助程序(T值)
{
Contract.Requires(!ReferenceEquals(value,null));
this.val=值;
}
公共虚拟T和
{ 
得到
{ 
确保(!ReferenceEquals(Contract.Result(),null));
返回此.val;
} 
}
[收缩变量法]
私有无效对象变量()
{
Contract.不变量(!ReferenceEquals(val,null));
}
}

ReferenceEquals(object,object)
不会对泛型类型发出警告,但保证它们不为null。

您是否尝试过
struct
约束?如果类型参数不受约束,您将如何实现
IsLessThan
?什么是语句
myNumber.IsGreaterThan(3)和.islesshan(10)甚至应该这样做?@pstrjds:不,我没有。但我会的@杰弗里:这只是两个例子,我不用看所有的测试就想到了。在
IsLessThan
之类的扩展方法上,我有类型约束,如果我没记错的话,类型必须是
IComparable
。该语句应该(使用测试框架)断言,当您大声读出它时,您所期望的是真的。您能否将行更改为如下,然后告诉我们这是否会影响错误:
((ReferenceAndHelper)myListOfCars.ShouldNotBeNull())和.ShouldBeA()您的代码与我的当前代码之间的唯一区别在于,您的字段为
只读
。此代码没有剪切它的原因是
t
可以是值类型,然后比较
value!=null
给出编译器警告(这告诉我应该有更好的方法来解决这个问题)。请参阅我更新的帖子,了解更多代码和关于这个问题的更多细节。你对如何解决这个问题有什么新的想法吗?让我重复一下我的理解:上面的代码是有效的,除非你在[ContractnVariantMethod]中得到警告,说“value!=null”不起作用,因为value可能是值类型。如果是这样的话,那么使用诸如:Contract.Invariant(And!=default(T)| | typeof(T).IsValueType)之类的东西怎么样?我刚刚在一个新的WPF项目中尝试了这一点,在那里我有一个带有不变量方法的泛型类,并且我添加了一个val!=null,并且我没有看到任何编译器警告。你收到了什么消息?@juda:为了再次检查,我刚刚创建了一个控制台应用程序,它只包含以下内容:上面代码中的类
和helper
以及
ReferenceAndHelper
,一个
Dummy
类和一个实例化
ReferenceAndHelper
并将
helper.和
打印到控制台窗口的程序。验证契约时,我收到一条警告“Contract invariant unproven:this.val!=null”,指向
ReferenceAndHelper的构造函数结尾的大括号,位置补码指向对
Contract.invariant()
的调用。我已尝试创建
public class StructAndHelper<T> : AndHelper<T>
    where T : struct
{
    public StructAndHelper(T value)
        : base(value)
    {
    }

    public override T And
    {
        get
        {
            return this.val;
        }
    }
}
public class AndHelper<T>
{
    protected readonly T val;
    public T And { get { return val; } }

    public AndHelper(T value)
    {
        Contract.Requires(value != null);
        val = value; 
    }

    [ContractInvariantMethod]
    void Invariants()
    {
        Contract.Invariant(And != null);
    }
}
public class AndHelper<T>
{
    private readonly T val;

    public AndHelper(T value)
    {
        Contract.Requires(!ReferenceEquals(value, null));

        this.val = value;
    }

    public virtual T And 
    { 
        get 
        { 
            Contract.Ensures(!ReferenceEquals(Contract.Result<T>(), null));

            return this.val; 
        } 
    }

    [ContractInvariantMethod]
    private void ObjectInvariant()
    {
        Contract.Invariant(!ReferenceEquals(val, null));
    }

}