开发者

C# HashSet union on IEnumerable within LINQ Func expression does not work (possible precompiler bug)

开发者 https://www.devze.com 2023-01-19 20:44 出处:网络
I am using using Microsoft .NET Framework 4.0. I have run into this using Aggregate on a Dictionary<T, List<T>> to extract the set of type T values used across all type List<T> list

I am using using Microsoft .NET Framework 4.0.

I have run into this using Aggregate on a Dictionary<T, List<T>> to extract the set of type T values used across all type List<T> lists in the dictionary. Here is the simplest case I could come up with that exhibits the same behaviour.

First, as the documentation states, the following does work:

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

That is, I can call Union on a HashSet with a List as the argument (since it implements IEnum开发者_如何学Cerable).

Whereas, the equivalent expression within a Func argument of the LINQ Aggregate expression produces an error (precompiler at least):

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

It expects the argument of Union to be HashSet, and will cooperate if it is given one, contrary to its behaviour outside LINQ/Func expressions.

The real world example I was using when I came across the problem was:

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

Which complains that it cannot convert IEnumerable<TVertex> to HashSet<TVertex>...


The problem here is in your understanding of the Select method. The lambda passed in does not recieve the list but instead elements of the list. So the variable you've named list is in fact of type int which is not compatible with Union.

Here is a more explicit example of what you're trying to do

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

With the type infererence removed it's much clearer why this doesn't work.


The problem actually lies in trying to replace the accumulator type of HashSet with the IEnumerable, .Union method doesn't add items to HashSet, but returns the new IEnumerable on the resulting union.

You should change your code to the following:

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

or

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


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

I think you would expect to use SelectMany here, unless you want the result to be IEnumerable<IEnumerable<T>>.

0

精彩评论

暂无评论...
验证码 换一张
取 消