Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有集合类型的CodeContracts_C#_C# 4.0_Collections_Code Contracts - Fatal编程技术网

C# 具有集合类型的CodeContracts

C# 具有集合类型的CodeContracts,c#,c#-4.0,collections,code-contracts,C#,C# 4.0,Collections,Code Contracts,我的类中有一个子项集合,并且有一个公共访问器。我想提供一个后置条件来确保集合中的项不为null(我知道,在测试2和3中,调用方可以更改集合,但现在我的目标只是确保从属性返回的集合不包含null项) 我认为使用假设和ForAll就足够了,但那没有帮助 下面是我尝试过的3个类的示例代码。 除第一次公开ReadOnlyObservableCollection、第二次公开ObservableCollection和第三次公开列表外,所有3种情况都是最相同的 -ReadOnlyObservableColle

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

我认为使用假设和ForAll就足够了,但那没有帮助

下面是我尝试过的3个类的示例代码。 除第一次公开ReadOnlyObservableCollection、第二次公开ObservableCollection和第三次公开列表外,所有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);
  }
}
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);
  }
}
为了避免每次使用
Children
属性时都编写
contract.aspect(child!=null)
,我是否可以定义一个contract


更新: 我尝试实现
枚举器
,以确保
Current
属性getter中的条件不为空,正如phoog所建议的那样。但警告仍然存在(令我惊讶)

公共类NotNullEnumerable:IEnumerable
{
私有IEnumerable enumerable;
公共NotNullEnumerable(IEnumerable enumerable)
{
_可枚举=可枚举;
}
#区域可数成员
公共IEnumerator GetEnumerator()
{
返回新的NotNullEnumerator(_enumerable.GetEnumerator());
}
#端区
#区域可数成员
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
返回GetEnumerator();
}
#端区
}
公共类NotNullEnumerator:IEnumerator
{
私有只读IEnumerator\u枚举器;
公共NotNullEnumerator(IEnumerator枚举器)
{
_枚举数=枚举数;
}
#区域IEnumerator成员
公共电流
{
得到
{
Contract.sure(Contract.Result()!=null);
返回_enumerator.Current;
}
}
#端区
#区域IDisposable成员
公共空间处置()
{
_枚举数。Dispose();
}
#端区
#区域IEnumerator成员
对象System.Collections.IEnumerator.Current
{
得到
{
Contract.sure(Contract.Result()!=null);
返回_enumerator.Current;
}
}
公共图书馆
{
返回_枚举数.MoveNext();
}
公共无效重置()
{
_枚举数。重置();
}
#端区
}
代码中的用法:

        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'
        }
test1t1=newtest1();
var NonNullTest1=新的NotNullEnumerable(t1.Children);
foreach(非NullTest1中的子级)
{
child.SomeMethod();//CodeContracts:可能对空引用“child”调用方法
}

有什么想法吗?

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

编辑:

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

编辑2:

由于
observeCollection
ReadOnlyObservableCollection
都装饰了
IList
实例,因此实现了
IList
,我尝试了以下方法。注意“确保未经证实”和“断言为假”之间的不一致性。无论
result
的静态类型是
ReadOnlyObservableCollection
还是
IList
,我都收到了相同的消息。我正在使用代码契约版本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;
            }
        }
    }
}
命名空间EnumerableContract
{
公共C类
{
公共整数长度{get;私有集;}
}
公共P类
{
公营儿童
{
得到
{
Contract.sure(Contract.Result()!=null);
Contract.assures(Contract.ForAll(Contract.Result(),c=>c!=null));
var result=new ReadOnlyObservableCollection(new ObservableCollection(new[]{new C()}));
假设(Contract.ForAll(result,c=>c!=null));
返回结果;//CodeContracts:确保未经验证的Contract.ForAll(Contract.result(),c=>c!=null)
}
}
公共课程
{
公共静态int Main(字符串[]args)
{
foreach(新P()子项中的变量项)
{
Assert(item==null);//CodeContracts:Assert为false
控制台写入线(项目长度);
}
返回0;
}
}
}
}
编辑3:


发现了一个很好的问题总结在;基本上,将附加条件添加到已实现接口的契约中违反了Liskov替换原则,因为这意味着具有附加限制的类不能用于接受实现该接口的对象的任何上下文中。

我尝试了相同的方法,我遇到的最多的是:

  • 声明只有一个属性确保返回非null值的泛型INonNullable接口,并在非null结构中实现它
  • 声明INonNullEnumerator和INonNullEnumerable接口(很可能是无用的),与IEnumerator和IEnumerable相同,但INonNullEnumerable具有IsEmpty属性,GetEnumerator要求它为false。NonNullEnumerator返回T,而不是inonnulable
  • 基于非Nullable数组声明实现INonNullEnumerable和IList的自定义集合(与common IEnumerable和common sense兼容)。IList imp方法
    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'
            }
    
    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;
                }
            }
        }
    }
    
      [ContractInvariantMethod]
      private void ObjectInvariant()
      {
        Contract.Invariant(_children != null && Contract.ForAll(_children, item => item != null));
        Contract.Invariant(_childrenReadOnly != null && Contract.ForAll(_childrenReadOnly, item => item != null);
      }