gpt4 book ai didi

c# - 具有集合类型的 CodeContracts

转载 作者:太空狗 更新时间:2023-10-29 21:55:20 27 4
gpt4 key购买 nike

我的类(class)有一个子项集合,我有一个公共(public)访问器。我想提供一个后置条件以确保集合中的项目不为空(我知道,在测试 2 和 3 中调用者可以更改集合,但现在我的目标只是确保从属性返回的集合不'包含项)。

我认为使用 Assume 和 ForAll 就足够了,但这没有帮助

这是我尝试过的包含 3 个类的示例代码。除了第一个公开 ReadOnlyObservableCollection、第二个 - ObservableCollection 和第三个 - List 之外,这 3 个案例几乎完全相同。

- ReadOnlyObservableCollection

class Test1
{
public Test1()
{
_children = new ObservableCollection<A>();
_childrenReadOnly = new ReadOnlyObservableCollection<A>(_children);
}

protected readonly ObservableCollection<A> _children;
protected readonly ReadOnlyObservableCollection<A> _childrenReadOnly;

public ReadOnlyObservableCollection<A> Children
{
get
{
Contract.Ensures(Contract.ForAll(Contract.Result<ReadOnlyObservableCollection<A>>(), i => i != null));
Contract.Assume(Contract.ForAll(_childrenReadOnly, i => i != null));
return _childrenReadOnly; // CodeContracts: ensures unproven: Contract.ForAll(Contract.Result<ReadOnlyObservableCollection<A>>(), i => i != null)
}
}

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

- ObservableCollection

class Test2
{
public Test2()
{
_children = new ObservableCollection<A>();
}

protected readonly ObservableCollection<A> _children;

public ObservableCollection<A> Children
{
get
{
Contract.Ensures(Contract.ForAll(Contract.Result<ObservableCollection<A>>(), i => i != null));
Contract.Assume(Contract.ForAll(_children, i => i != null));
return _children; // CodeContracts: ensures unproven: Contract.ForAll(Contract.Result<ObservableCollection<A>>(), i => i != null)
}
}

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

- 列表

class Test3
{
protected readonly List<A> _children = new List<A>();

public List<A> Children
{
get
{
Contract.Ensures(Contract.ForAll(Contract.Result<List<A>>(), i => i != null));
Contract.Assume(Contract.ForAll(_children, i => i != null));
return _children; // No warning here when using List instead of ObservableCollection
}
}

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

下面是使用这个类的测试代码:

  Test1 t1 = new Test1();
foreach (A child in t1.Children)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}

Test2 t2 = new Test2();
foreach (A child in t2.Children)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}

Test3 t3 = new Test3();
foreach (A child in t3.Children)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}

我能否以某种方式定义契约,以免每次使用 Children 属性时都编写 Contract.Assume(child != null)


更新:

我尝试实现 Enumerator 以确保 Current 属性 getter 中的非空条件,正如 phoog 所建议的那样。但是警告仍然存在(令我感到惊讶)。

public class NotNullEnumerable<T> : IEnumerable<T>
{
private IEnumerable<T> _enumerable;
public NotNullEnumerable(IEnumerable<T> enumerable)
{
_enumerable = enumerable;
}

#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return new NotNullEnumerator<T>(_enumerable.GetEnumerator());
}
#endregion

#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}

public class NotNullEnumerator<T> : IEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;
public NotNullEnumerator(IEnumerator<T> enumerator)
{
_enumerator = enumerator;
}

#region IEnumerator<T> Members
public T Current
{
get
{
Contract.Ensures(Contract.Result<T>() != null);
return _enumerator.Current;
}
}
#endregion

#region IDisposable Members
public void Dispose()
{
_enumerator.Dispose();
}
#endregion

#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get
{
Contract.Ensures(Contract.Result<object>() != null);
return _enumerator.Current;
}
}

public bool MoveNext()
{
return _enumerator.MoveNext();
}

public void Reset()
{
_enumerator.Reset();
}
#endregion
}

代码中的用法:

        Test1 t1 = new Test1();
var NonNullTest1 = new NotNullEnumerable<A>(t1.Children);
foreach (A child in NonNullTest1)
{
child.SomeMethod(); // CodeContracts: Possibly calling a method on a null reference 'child'
}

有什么想法吗?

最佳答案

我会创建自己的集合类型。例如,您可以实现 IList<T>并“确保”索引 getter 永远不会返回 null,并且“要求”Add()并且索引 setter 从不接收 null 作为参数。

编辑:

为了避免在 foreach 循环中出现“可能在空引用上调用方法”消息,您可能还必须实现自己的枚举器类型并“确保”它的 Current属性从不返回 null。

编辑2:

ObservableCollection<>ReadOnlyObservableCollection<>都装饰一个 IList<>实例并因此实现 IList<> ,我尝试了以下。请注意“确保未经证实”和“断言为假”之间的不一致。无论是 result 的静态类型,我都收到了相同的消息是ReadOnlyObservableCollection<C>IList<C> .我使用的是 Code Contracts 版本 1.4.40602.0。

namespace EnumerableContract
{
public class C
{
public int Length { get; private set; }
}

public class P
{
public IList<C> Children
{
get
{
Contract.Ensures(Contract.Result<IList<C>>() != null);
Contract.Ensures(Contract.ForAll(Contract.Result<IList<C>>(), c => c != null));

var result = new ReadOnlyObservableCollection<C>(new ObservableCollection<C>(new[] { new C() }));

Contract.Assume(Contract.ForAll(result, c => c != null));

return result; //CodeContracts: ensures unproven Contract.ForAll(Contract.Result<IList<C>>(), c => c != null)
}
}

public class Program
{
public static int Main(string[] args)
{
foreach (var item in new P().Children)
{
Contract.Assert(item == null); //CodeContracts: assert is false
Console.WriteLine(item.Length);
}

return 0;
}
}
}
}

编辑3:

http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/af403bbc-ca4e-4546-8b7a-3fb3dba4bb4a/ 找到了很好的问题总结;基本上,向已实现接口(interface)的契约添加额外条件违反了 Liskov 替换原则,因为这意味着具有额外限制的类不能在接受实现该接口(interface)的对象的任何上下文中使用。

关于c# - 具有集合类型的 CodeContracts,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8272311/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com