【问题标题】:C# HashSet union on IEnumerable within LINQ Func expression does not work (possible precompiler bug)LINQ Func 表达式中 IEnumerable 上的 C# HashSet 联合不起作用(可能的预编译器错误)
【发布时间】:2010-10-14 16:53:16
【问题描述】:

我正在使用 Microsoft .NET Framework 4.0。

我在Dictionary<T, List<T>> 上使用聚合来提取字典中所有类型List<T> 列表中使用的T 类型值集。这是我能想到的最简单的情况,它表现出相同的行为。

首先,正如documentation 所述,以下确实有效:

var set = new HashSet<int>();
var list = new LinkedList<int>();
var newSet = set.Union(list);

也就是说,我可以在HashSet 上调用Union,并以List 作为参数(因为它实现了IEnumerable)。

然而,LINQ Aggregate 表达式的 Func 参数中的等效表达式会产生错误(至少是预编译器):

new List<int>[] { new List<int>() }.Aggregate(new HashSet<int>(), (acc, list) => acc.Union(list));

它期望 Union 的参数是 HashSet,如果给它一个参数,它就会合作,这与它在 LINQ/Func 表达式之外的行为相反。

我遇到问题时使用的真实示例是:

public AdjacencyListGraph(Dictionary<TVertex, LinkedList<TVertex>> adjacencyList)
{
    var vertices = adjacencyList.Aggregate(new HashSet<TVertex>(),
    (vertices, list) => vertices.Union(list.Value));
}

抱怨它无法将IEnumerable&lt;TVertex&gt; 转换为HashSet&lt;TVertex&gt;...

【问题讨论】:

  • 我很好奇,“预编译器”是什么意思?

标签: linq c#-4.0 union aggregate hashset


【解决方案1】:

这里的问题在于您对Select 方法的理解。传入的 lambda 不接收列表,而是接收列表的元素。因此,您命名为list 的变量实际上是int 类型,它与Union 不兼容。

这是一个更明确的示例,说明您正在尝试做什么

new List<int>().Select( (int list) => new HashSet<int>().Union(list));

删除类型推断后,为什么这不起作用就更清楚了。

【讨论】:

  • 你说的很对,我在上面的例子中错误地使用了 Select,它应该是这样的: new List[] {new List()}.Select (list => new HashSet().Union(list));在我的真实世界案例中,您会看到该列表是真实的列表。
【解决方案2】:

问题实际上在于试图用 IEnumerable 替换 HashSet 的累加器类型,.Union 方法不会将项添加到 HashSet,而是在生成的 union 上返回新的 IEnumerable。

您应该将代码更改为以下内容:

    // select from keys
    var vertices = new HashSet<TVertex>(adjacencyList.Keys);

    // select from values
    var vertices = new HashSet<TVertex>(adjacencyList.SelectMany(dictEntry => dictEntry.Value));

【讨论】:

  • 你的第一个例子不会产生我的表达想要达到的效果。我希望合并所有 adjacencyList 的值,它们是列表,而不是获取这些列表的索引。
  • 这就是我添加第二个的原因。无法从错误的代码中推断出您的意图。
  • 对不起,我正要回复,第二个做我想要的,谢谢。
  • 但是我不知道为什么我不能做我最初做的事情。我打算更换蓄能器......就像在红宝石注入中一样
  • 啊...以下确实有效: var vs = adjacencyList.Aggregate((IEnumerable) new HashSet(), (vertices, list) => vertices.Union(list .值));
【解决方案3】:
new List<int>().Select(list => new HashSet<int>().Union(list));

我想你会希望在这里使用SelectMany,除非你希望结果是IEnumerable&lt;IEnumerable&lt;T&gt;&gt;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多