List.AddRange使用IEnumerable<T>参数无效?

7

我有一个场景,需要将一些项目添加到列表中...

List<T> items = new List<T>();
IEnumerable<T> addItems = someCollection.Where(...);
items.AddRange(addItems);

使用此代码,列表中不会添加任何项目,但是如果在Linq语句后添加.ToList(),则项目将正确添加。我猜这是由于延迟执行造成的,但是我认为由于List.AddRange函数接受IEnumerable,因此它应该枚举要添加的项。有人可以请解释一下为什么会发生这种情况吗?

1
你是怎么看到“没有添加任何项目”的?因为它应该添加项目的。 - Reed Copsey
someCollection的类型是什么?请提供实际可行的代码,我不太确定ToList需要添加在哪里。someCollection是否为IQueryable,并且可能存在未正确实现的Linq提供程序吗? - Mike Zboray
4个回答

4
我猜这是由于延迟执行引起的,但我认为由于List.AddRange函数接受IEnumerable,它应该枚举要添加的项。

确实如此。对于ICollection<T>(在这种情况下不会触发),有一个短路,它将导致它使用ICollection<T>.CopyTo而不是枚举项,但否则,它将枚举集合。

要查看工作示例,请尝试:

using System;
using System.Linq;
using System.Collections.Generic;

internal class Program
{
    private static List<T> RunQuery<T>(IEnumerable<T> someCollection, Func<T, bool> predicate)
    {
        List<T> items = new List<T>();
        IEnumerable<T> addItems = someCollection.Where(predicate);
        items.AddRange(addItems);
        return items;
    }

    static void Main()
    {
        var values = Enumerable.Range(0, 1000);

        List<int> results = RunQuery(values, i => i >= 500);

        Console.WriteLine(results.Count);
        Console.WriteLine("Press key to exit:");
        Console.ReadKey();
    }
}

这将使用您的精确代码,并打印出500(List<T>中的正确项目数量)。

也许在 OP 的例子中,someCollection 是某个类型,该类型已实现了一个空定义的 CopyTo 方法? - Dan Tao
@DanTao 是的 - 可能还有其他问题。但是,总的来说,这确实可以与 IEnumerable<T> 正常工作 - 这不是一个“延迟执行”的问题。 - Reed Copsey
@DanTao(请注意,这需要OP拥有自己的“Where”方法,以使用奇怪的实现创建ICollection<T>) - Reed Copsey
好的。看起来更有可能是楼主对某些事情弄错了...但谁知道呢。 - Dan Tao

3

我本以为由于List.AddRange函数接受IEnumerable,它会枚举要添加的项目。

我尝试了下面的代码,AddRange(IEnumerable<T>)确实有效。

List<string> someCollection = new List<string>{"A", "B", "C"};
List<string> items = new List<string>();
IEnumerable<string> addItems = someCollection.Where(x => x != "");
items.AddRange(addItems);

2

它确实有效。这是一个证明的单元测试:

[TestFixture]
public class AddRangeTest
{
    [Test]
    public void AddRange()
    {
        var list = new List<int>();
        var someCollection = new List<int> { 1, 2, 3 };
        var subItems = someCollection.Where(x => x > 1);
        list.AddRange(subItems);
        Assert.AreEqual(2, list.Count);
    }
}

也许在您的特定情况下,有些东西无法正常工作。

2

谢谢回复。我尝试简化了这个例子的代码,但通常细节问题比较棘手!

在 .Where() 语句和 AddRange() 调用之间,代码(深层次)清空了源列表(在此示例中为“items”)。开发人员没有意识到筛选器被推迟到 AddRange() 调用时,他们已经清空了源列表。

很高兴知道我没有搞错 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接