自定义 foreach 中的 yield return 未按预期工作
yield return in custom foreach not working as expected
我有一个重写的 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
也是我的 class,具有一些共同属性,例如 CreatedOn
,并且每个实体都继承自此 class。此重写方法使用我自己的 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 在这次迭代中不执行它?
延迟执行。代码在您消耗产生的项目时执行,例如。通过遍历结果或在其上调用 .ToList()
。在您的代码示例中,您似乎从不使用迭代的结果,因此它永远不会被执行。
因为迭代器需要迭代
致电ToList();
You use a yield return statement to return each element one at
a time.
You consume an iterator method by using a foreach statement or LINQ
query. Each iteration of the foreach loop calls the iterator method.
When a yield return statement is reached in the iterator method,
expression is returned, and the current location in code is retained.
Execution is restarted from that location the next time that the
iterator function is called.
简而言之
对 ForEach
的调用不会执行方法 的主体。相反,调用 returns 一个 IEnumerable,因为没有任何枚举,该方法甚至根本没有击中正文,因为它从未开始迭代。
如果您使用 yield return
或 yield break
,您的代码将仅在有人迭代您从函数获得的 IEnumerable<T>
时执行。
yield
运算符使编写惰性代码成为可能。
在语句中追加.ToList()
或.ToArray()
,但我推荐.ToList()
,因为.ToArray()
需要创建多个数组,如果参数不是ICollection<T>
。
See
我有一个重写的 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
也是我的 class,具有一些共同属性,例如 CreatedOn
,并且每个实体都继承自此 class。此重写方法使用我自己的 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 在这次迭代中不执行它?
延迟执行。代码在您消耗产生的项目时执行,例如。通过遍历结果或在其上调用 .ToList()
。在您的代码示例中,您似乎从不使用迭代的结果,因此它永远不会被执行。
因为迭代器需要迭代
致电ToList();
You use a yield return statement to return each element one at a time.
You consume an iterator method by using a foreach statement or LINQ query. Each iteration of the foreach loop calls the iterator method. When a yield return statement is reached in the iterator method, expression is returned, and the current location in code is retained. Execution is restarted from that location the next time that the iterator function is called.
简而言之
对 ForEach
的调用不会执行方法 的主体。相反,调用 returns 一个 IEnumerable,因为没有任何枚举,该方法甚至根本没有击中正文,因为它从未开始迭代。
如果您使用 yield return
或 yield break
,您的代码将仅在有人迭代您从函数获得的 IEnumerable<T>
时执行。
yield
运算符使编写惰性代码成为可能。
在语句中追加.ToList()
或.ToArray()
,但我推荐.ToList()
,因为.ToArray()
需要创建多个数组,如果参数不是ICollection<T>
。
See