自定义foreach中的yield return不按预期工作

问题描述 投票:0回答:3

我有一个重写的SaveChangesAsync EF方法如下:

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        ChangeTracker
            .Entries()
            .Where(e => e.State == EntityState.Added)
            .Select(e => e.Entity as BaseEntity)
            .ForEach(e => e.ModifiedOn = e.CreatedOn = DateTimeOffset.Now);

        return base.SaveChangesAsync(cancellationToken);
    }

BaseEntity也是我的类,有一些常见的属性,如CreatedOn,每个实体都继承自这个类。这个覆盖的方法使用我自己的ForEach扩展:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        foreach (var item in enumerable)
        {
            action(item);
            yield return item;
        }
    }

当存在yield语句时,foreach循环不会执行。如果我改成它:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        foreach (var item in enumerable)
        {
            action(item);
        }

        return enumerable;
    }

然后它完美地运作。为什么yield不会在迭代中执行它?

c# yield
3个回答
4
投票

延期执行。代码在您使用产生的项目时执行,例如。通过循环结果或通过调用.ToList()。在您的代码示例中,您似乎永远不会消耗迭代的结果,因此永远不会执行它。


2
投票

因为迭代器需要迭代

打电话给ToList();

yield (C# Reference)

您使用yield return语句一次返回一个元素。

您可以使用foreach语句或LINQ查询来使用迭代器方法。 foreach循环的每次迭代都会调用迭代器方法。在迭代器方法中达到yield return语句时,将返回表达式,并保留代码中的当前位置。下次调用迭代器函数时,将从该位置重新开始执行。

简而言之

ForEach的调用不执行该方法的主体。相反,该调用返回一个IEnumerable,因为没有对它进行枚举,所以该方法甚至根本没有打到正文,因为它从未开始迭代。


1
投票

如果你使用yield returnyield break,你的代码只会在有人迭代你从函数获得的IEnumerable<T>时被执行。 yieldoperator可以编写惰性代码。

.ToList().ToArray()附加到语句中,但我建议使用.ToList(),因为如果参数不是.ToArray()类型,ICollection<T>需要创建多个数组。 See

© www.soinside.com 2019 - 2024. All rights reserved.