ForEach扩展方法未使用Dictionary调用

时间:2018-06-14 02:41:25

标签: c#

我的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扩展方法不同,第一个[我的]永远不会被调用。)

2 个答案:

答案 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));