【问题标题】:IEnumerable<T> Has Results Until Count() Or Any() Are CalledIEnumerable<T> 在调用 Count() 或 Any() 之前有结果
【发布时间】:2014-03-26 18:31:15
【问题描述】:

我正在对 Linq 扩展方法的变体进行性能测试,遇到了一个奇怪的情况。

执行返回测试时,先调用Count()会返回1,后面的Any()为false。

先调用Any()时为真,后面的Count()为0。

调用任一方法之前的断点检查显示有 1 个项目符合预期,但在以这种方式或调用 Any() 或 Count() 枚举后,可枚举项为空。

有人可以解释这种行为吗?由于延迟执行的一些警告,我的实现中是否存在错误?

public class Thing
{
    public Guid Id { get; set; }
}

[TestClass]
public class IEnumerableExtensionsTests
{
    Guid[] thingKeys = new Guid[1] { Guid.Parse("11A1AA1A-1A11-A111-AA11-111111AA1A11") };
    System.Collections.ObjectModel.Collection<Thing> things= new System.Collections.ObjectModel.Collection<Thing>();
    int additionalThingCount = 100;

    [TestMethod]
    public void TestIntersect1()
    {
        DateTime start = DateTime.Now;
        var exceptionsList = things.Intersect1(thingKeys, (e) => e.Id);
        //int count1 = exceptionsList.Count();
        //Assert.AreEqual<int>(thingKeys.Length, count1);
        bool any1 = exceptionsList.Any();
        int count2 = exceptionsList.Count();
        bool any2 = exceptionsList.Any();
        string key = thingKeys[0].ToString();
        var first = exceptionsList.FirstOrDefault();
        var result = exceptionsList.FirstOrDefault(e => e.Id.ToString() == key);
        var duration = DateTime.Now - start;
        Debug.WriteLine(string.Format("TestIntersect1 duration {0}", duration));
        Assert.IsNotNull(result);
    }

    [TestInitialize]
    public void TestInit()
    {
        foreach(var key in thingKeys)
        {
            things.Add(new Thing()
            {
                Id = key
            });
        };
        for (int i1 = 0; i1 < additionalThingCount; i1++)
        {
            things.Add(new Thing()
            {
                Id = Guid.NewGuid()
            });
        }
    }
}

public static class IEnumerableExtension
{
    public static IEnumerable<T> Intersect1<T, Y>(this IEnumerable<T> items, IEnumerable<Y> keys, Func<T, Y> firstMemberSelector)
    {
        var hashset = new HashSet<Y>(keys);
        var returnValue = items.Where(t => hashset.Remove(firstMemberSelector(t)));
        return returnValue;
    }
}

【问题讨论】:

  • 这“修复”了它(我还没有在我的代码中寻找新的副作用):msmvps.com/blogs/jon_skeet/archive/2008/02/28/… var hashset = new HashSet(keys); var returnValue = items.Where(t => hashset.Remove(firstMemberSelector(t))); foreach (var item in returnValue) { yield return item; }

标签: c# linq ienumerable deferred-execution


【解决方案1】:

每次迭代结果时,您都将调用 Where 过滤器... 它会从 hashset 中删除项目

所以在它迭代一次之后,hashset 将不再有任何这些项目,所以没有任何东西可以返回。

基本上,您观察到的是带有副作用的 Where 子句是个坏主意。

您可能希望使用Join 来代替执行交集:

return items.Join(keys, firstMemberSelector, key => key, (value, key) => value);

完全不一样,因为它不会是一个集合操作......但你可以解决这个问题。

【讨论】:

  • 没错,重复枚举会重复产生/延迟的 LINQ 代码。您的代码不是幂等的
  • 加入版本是我正在测试的那个集合中的另一个版本:)。所以 hashset 就是一遍又一遍地使用的同一个副本?
  • @ChrisKissinger:当然。你只调用了一次Intersect1,毕竟你没有使用迭代器块......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多