IEnumerable<T> имеет результаты, пока не будут вызваны Count() или Any()

Я тестирую изменения в методах расширения 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;
    }
}

1 ответ

Решение

Каждый раз, когда вы перебираете результат, вы будете называть это Where фильтр... который удаляет элементы из hashset как это происходит.

Так что после того, как это повторяется один раз, hashset больше не будет ни одного из этих предметов, так что больше нечего будет возвращать.

По сути, вы наблюдаете, что Where оговорка с побочным эффектом - плохая идея.

Вы можете использовать Join вместо этого выполнить пересечение:

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

Это не совсем то же самое, в том смысле, что это не будет заданная операция... но вы можете это исправить.

Другие вопросы по тегам