如何通过契约来定义IEnumerable的行为呢? [英] How to define IEnumerable behavior by contract?

查看:204
本文介绍了如何通过契约来定义IEnumerable的行为呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这2种方法,它返回IEnumerable的:

 私人的IEnumerable< MyClass的> GetYieldResult(INT qtResult)
    {
        的for(int i = 0; I< qtResult;我++)
        {
            算上++;
            产量返回新MyClass的(){n = I + 1};
        }
    }

    私人的IEnumerable< MyClass的> GetNonYieldResult(INT qtResult)
    {
        VAR的结果=新名单,其中,MyClass的>();

        的for(int i = 0; I< qtResult;我++)
        {
            算上++;
            result.Add(新MyClass的(){n = i + 1的});
        }

        返回结果;
    }
 

这code显示2个不同的行为,呼吁IEnumerable的一些方法时:

  [TestMethod的]
    公共无效的Test1()
    {
        计数= 0;

        IEnumerable的< MyClass的> yieldResult = GetYieldResult(1);

        变种firstGet = yieldResult.First();
        变种secondGet = yieldResult.First();

        Assert.AreEqual(1,firstGet.Id);
        Assert.AreEqual(1,secondGet.Id);

        Assert.AreEqual(2,计数); //调用第一()2次,yieldResult创建2次
        Assert.AreNotSame(firstGet,secondGet); //创建和各列表项的不同实例
    }

    [测试方法]
    公共无效的Test2()
    {
        计数= 0;

        IEnumerable的< MyClass的> yieldResult = GetNonYieldResult(1);

        变种firstGet = yieldResult.First();
        变种secondGet = yieldResult.First();

        Assert.AreEqual(1,firstGet.Id);
        Assert.AreEqual(1,secondGet.Id);

        Assert.AreEqual(1,计数); //如预期,它会创建只有1结果集
        Assert.AreSame(firstGet,secondGet); //并调用第一()多次将始终返回的MyClass同一个实例
    }
 

很简单,选择我想要的行为,当我的code返回IEnumerables,但我怎么能明确定义了一些方法获取创建一个单一的结果集dispite它多少次所谓的首先一个IEnumerable作为参数( )的方法。

当然,我不希望强制不必要地创建的所有itens,我要定义的参数为IEnumerable说没有的项目将被列入或从集合中删除。

编辑:只是要清楚,问题不在于如何产量的作品为什么IEnumerable的可以返回不同的情况下,每个呼叫。现在的问题是我怎么可以指定一个参数应该是一个只搜索的集合,返回的MyClass时,我喜欢称之为第一()或以(1)数次。

方法相同的情况下,

任何想法?

在此先感谢!

解决方案
  

当然,我不想强​​迫所有itens不必要地创建

在这种情况下,你需要允许该方法根据需要创建它们,如果对象是按需创建(如果没有某种形式的缓存),他们会的不同的的对象(至少在被不同的引用和mdash感;平等无价值对象的默认定义)

如果你的对象本身是独特的(也就是他们不定义一些基础的价值相等),那么每次调用将创建一个不同的对象(无论构造函数参数)

因此​​,答案为

  

但我怎么能明确定义了一些方法获取创建一个单一的结果集dispite它多少次调用一个IEnumerable作为参数优先()方法。

是你不能,除了通过创建一组对象,并通过定义平等是不同的东西重复返回同一组,的。


附加(基于评论)。如果你真的希望能够重播(对于想一个更好的词)同一组对象的不建全收集,你可以想缓存已经生成和重放,第一。是这样的:

 私有静态列表<迈德特>缓存=新的名单,其中,迈德特>();
公开的IEnumerable<迈德特>的GetData(){
  的foreach(VAR d在缓存){
    产生回报D组;
  }

  VAR位置= cache.Count;

  而(maxItens<位置){
    迈德特下一= MakeNextItem(位置);
    cache.Add(下);
    得到的回报下一个;
  }
}
 

我希望这将有可能围绕一个迭代建立这样一个缓存包装以及(,而将成为的foreach 在底层的迭代器,但你需要缓存的迭代器或到需要的位置,如果主叫迭代超越cahing 列表)。

NB 任何缓存的方法是很难使线程安全的。

Consider this 2 methods that returns IEnumerable:

    private IEnumerable<MyClass> GetYieldResult(int qtResult)
    {
        for (int i = 0; i < qtResult; i++)
        {
            count++;
            yield return new MyClass() { Id = i+1 };
        }
    }

    private IEnumerable<MyClass> GetNonYieldResult(int qtResult)
    {
        var result = new List<MyClass>();

        for (int i = 0; i < qtResult; i++)
        {
            count++;
            result.Add(new MyClass() { Id = i + 1 });
        }

        return result;
    }

This code shows 2 different behaviors when calling some method of IEnumerable:

    [TestMethod]
    public void Test1()
    {
        count = 0;

        IEnumerable<MyClass> yieldResult = GetYieldResult(1);

        var firstGet = yieldResult.First();
        var secondGet = yieldResult.First();

        Assert.AreEqual(1, firstGet.Id);
        Assert.AreEqual(1, secondGet.Id);

        Assert.AreEqual(2, count);//calling "First()" 2 times, yieldResult is created 2 times
        Assert.AreNotSame(firstGet, secondGet);//and created different instances of each list item
    }

    [TestMethod]
    public void Test2()
    {
        count = 0;

        IEnumerable<MyClass> yieldResult = GetNonYieldResult(1);

        var firstGet = yieldResult.First();
        var secondGet = yieldResult.First();

        Assert.AreEqual(1, firstGet.Id);
        Assert.AreEqual(1, secondGet.Id);

        Assert.AreEqual(1, count);//as expected, it creates only 1 result set
        Assert.AreSame(firstGet, secondGet);//and calling "First()" several times will always return same instance of MyClass
    }

It's simple to choose which behavior I want when my code returns IEnumerables, but how can I explicitly define that some method gets an IEnumerable as parameter that creates a single result set dispite of how many times it calls "First()" method.

Of course, I don't want to force all itens to be created unnecessarily and I want to define the parameter as IEnumerable to say that no item will be included or removed from the collection.

EDIT: Just to be clear, the question is not about how yield works or why IEnumerable can return different instances for each call. The question is how can I specify that a parameter should be a "search only" collection that returns same instances of MyClass when I call methods like "First()" or "Take(1)" several times.

Any ideas?

Thanks in advance!

解决方案

Of course, I don't want to force all itens to be created unnecessarily

In which case you need to allow the method to create them on demand, and if objects are created on demand (and without some form of cache) they will be different objects (at least in the sense of being different references—the default definition of equality for non-value objects).

If your objects are inherently unique (i.e. they don't define some value based equality) then each call to new will create a different object (whatever the constructor parameters).

So the answer to

but how can I explicitly define that some method gets an IEnumerable as parameter that creates a single result set dispite of how many times it calls "First()" method.

is "you can't" except by creating one set of objects and repeatedly returning the same set, or by defining equality to be something different.


Additional (based on comments). If you really want to be able to replay (for want of a better term) the same set of objects without building the whole collection you could cache want has already been generated and replay that first. Something like:

private static List<MyData> cache = new List<MyData>();
public IEnumerable<MyData> GetData() {
  foreach (var d in cache) {
    yield return d;
  }

  var position = cache.Count;

  while (maxItens < position) {
    MyData next = MakeNextItem(position);
    cache.Add(next);
    yield return next;
  }
}

I expect it would be possible to build such a caching wrapper around an iterator as well (while would become foreach over underlying iterator, but you would need to cache that iterator or Skip to the require position if the caller iterated beyond the cahing List).

NB any caching approach would be hard to make thread safe.

这篇关于如何通过契约来定义IEnumerable的行为呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆