我的Dictionary
包含ValueTuple
:
private static readonly Dictionary<RELAY, (byte RelayNumber, bool IsClosed)> s_relays = new Dictionary<RELAY, (byte RelayNumber, bool IsClosed)>
{
{ RELAY.OUTPUT1, (4, false) },
{ RELAY.OUTPUT2, (9, false) },
{ RELAY.OUTPUT3, (11, false) },
};
稍后在我的代码中,我将IsClosed
设置为true,用于一个或多个中继。我为ForEach
编写了Dictionary
扩展方法:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
Debug.Assert(enumeration != null, $"Argument {nameof(enumeration)} cannot be null!");
Debug.Assert(action != null, $"Argument {nameof(action)} cannot be null!");
foreach (T item in enumeration)
{
action(item);
yield return item;
}
}
我想为每个关闭的中继调用一个方法。例如,写信给Console.WriteLine
:
Relays.Where(x => x.Value.IsClosed).ForEach(x => Console.WriteLine(x.Key));
但这不起作用。但是,如果我包含ToList()
下面的代码
Relays.Where(x => x.Value.IsClosed).ToList().ForEach(x => Console.WriteLine(x.Key));
我错过了什么?
(我确实意识到这两个示例之间调用的ForEach
扩展方法不同,第一个[我的]永远不会被调用。)
答案 0 :(得分:3)
在这种情况下,与通过使用List<T>.ForEach()
模式迭代集合的yield return
实现不同,您的.ForEach
实现实际上是延迟执行&#34;过滤器&#34;在查询完全解析之前不会执行。
在这种意义上,它与.Where()
方法非常相似,只是它不是减少查询集,而是在解析查询时对它执行副作用操作。在枚举枚举之前,两者都不会被执行。
在查询末尾添加.ToList()
将按预期执行您的方法并解析查询。
或者,如果您只是想要一种方便的方法来迭代并对集合执行操作,您可以删除yield return
并简单地迭代集合并在最后返回它:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
Debug.Assert(enumeration != null, $"Argument {nameof(enumeration)} cannot be null!");
Debug.Assert(action != null, $"Argument {nameof(action)} cannot be null!");
foreach (T item in enumeration)
{
action(item);
}
return enumeration;
}
然而,存在的风险是您可能会枚举两次。
值得注意的是你的第二个例子
Relays.Where(x => x.Value.IsClosed).ToList().ForEach(x => Console.WriteLine(x.Key));
实际上并未调用您的扩展方法。相反,它调用List<T>.ForEach()
方法。
答案 1 :(得分:1)
这只是一个查询,它只会在需要时“懒洋洋地”执行。让我们简单一点,这样我们就可以看到会发生什么:
private static readonly List<int> nums = new List<int> { 1, 2, 3 };
public static IEnumerable<int> MyEach(this IEnumerable<int> enumeration, Action<int> action)
{
foreach (var item in enumeration)
{
action(item);
yield return item;
}
}
这只是一个查询,所以没有任何东西可以执行:
var query = nums.Where(x => x == 1).MyEach(x => Console.WriteLine(x));
但是一旦你这样做了:
query.ToList();
它将执行MyEach
扩展方法。
你必须要小心,因为它取决于它何时被执行。例如,试试这个,你会看到数字1打印到控制台3次:
var query = nums.Where(x => x == 1).MyEach(x => Console.WriteLine(x));
nums.Add(1);
nums.Add(1);
query.ToList();
但是,如果您删除yield
,则会立即执行:
public static void MyEach(this IEnumerable<int> enumeration, Action<int> action)
{
foreach (var item in enumeration)
{
action(item);
//yield return item;
}
}
// Now it will execute right away
nums.Where(x => x == 1).MyEach(x => Console.WriteLine(x));