Linq的执行顺序是这次捕获的原因吗?

时间:2012-11-21 19:26:56

标签: c# linq deferred-execution

我有这个功能重复一个序列:

public static List<T> Repeat<T>(this IEnumerable<T> lst, int count)
{
    if (count < 0)
        throw new ArgumentOutOfRangeException("count");

    var ret = Enumerable.Empty<T>();

    for (var i = 0; i < count; i++)
        ret = ret.Concat(lst);

    return ret.ToList();
}

现在,如果我这样做:

var d = Enumerable.Range(1, 100);
var f = d.Select(t => new Person()).Repeat(10); 
int i = f.Distinct().Count();

我希望i为100,但它给我1000!我的问题严格来说是为什么会发生这种情况? Linq难道不应该足够聪明地弄清楚它是我需要与变量ret连接的第一个选定的100个人吗?我感觉ConcatSelect执行时ret.ToList()var f = d.Select(t => new Person()).ToList().Repeat(10); int i = f.Distinct().Count(); //prints 100 一起使用时,会优先考虑Equals

修改

如果我这样做,我会按预期得到正确的结果:

{{1}}

再次修改:

我没有覆盖{{1}}。我只想尝试获得100个独特的人(当然可以参考)。我的问题是有人可以向我解释为什么Linq没有先进行选择操作然后连接(当然在执行时)?

3 个答案:

答案 0 :(得分:4)

问题在于,除非您致电ToList,否则每次d.Select(t => new Person())通过列表时都会重新枚举Repeat,从而创建重复的Person。该技术称为deferred execution

通常,LINQ并不假设每次枚举序列时它都会得到相同的序列,甚至是相同长度的序列。如果不希望出现这种影响,您可以随时通过立即调用Repeat来“实现”ToList方法中的序列,如下所示:

public static List<T> Repeat<T>(this IEnumerable<T> lstEnum, int count) {
    if (count < 0)
        throw new ArgumentOutOfRangeException("count");

    var lst = lstEnum.ToList(); // Enumerate only once
    var ret = Enumerable.Empty<T>();

    for (var i = 0; i < count; i++)
        ret = ret.Concat(lst);

    return ret.ToList();
}

答案 1 :(得分:1)

我可以将我的问题分解为不那么简单的事情:

var d = Enumerable.Range(1, 100);
var f = d.Select(t => new Person());

现在基本上我正在这样做:

f = f.Concat(f);

请注意,查询至今尚未执行。在执行时,f仍然是d.Select(t => new Person()) 未执行。因此,执行时的最后一条陈述可以分解为:

f = f.Concat(f); 
//which is 
f = d.Select(t => new Person()).Concat(d.Select(t => new Person()));

显然可以创建100 + 100 = 200个新的人员实例。所以

f.Distinct().ToList(); //yields 200, not 100

这是正确的行为。

编辑:我可以重写扩展方法,就像

一样简单
public static IEnumerable<T> Repeat<T>(this IEnumerable<T> source, int times)
{
    source = source.ToArray();
    return Enumerable.Range(0, times).SelectMany(_ => source);
}

我使用了dasblinkenlight的建议来解决问题。

答案 2 :(得分:0)

每个Person对象都是一个单独的对象。所有1000都是截然不同的。

Person类型的相等定义是什么?如果不覆盖它,那么该定义将是引用相等,这意味着所有1000个对象都是不同的。