Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use the ValueTuple naming feature with anonymous methods?

I would like to use the naming feature of ValueTuple as follows:

     IEnumerable<(string, char, int)> valueTuples = new(string, char, int)[]
     {
        ("First", '1', 1),
        ("Second", '2', 2),
        ("Third", '3', 3)
     };

     var projection1 = valueTuples.Select(((string s, char c, int i) tuple) => tuple.i);

But it does not compile with an error message that is not very helpful. However these both compile:

     var projection2 = valueTuples.Select(tuple => tuple.Item1);

     var projection3 = valueTuples.Select(((string, char, int) tuple) => tuple.Item1);

Trying a more direct approach, this does NOT compile BUT gives an error message that is more helpful:

     var projection4 = Enumerable.Select(
        valueTuples, ((string s, char c, int i) tuple) => tuple.i);

Which leads to trying this, which compiles:

     var projection5 = Enumerable.Select<(string, char, int), int>(
        valueTuples, ((string s, char c, int i) tuple) => tuple.i);

Which finally inspires this, which compiles:

     var projection6 = valueTuples.Select<(string, char, int), int>(
        ((string s, char c, int i) tuple) => tuple.i);

Is this a general issue with IEnumerable extension methods? It would appear not because this compiles:

     var filtered = valueTuples.Where(((string s, char c, int i) tuple) => tuple.i > 1);

Why do I have to resort to projection6 syntax to get this to work?

like image 416
mikevg Avatar asked Apr 28 '18 04:04

mikevg


1 Answers

You have a list of unnamed tuples (string, char, int) (note there are no names). For that reason, access using default names Item1, Item2 and so on works fine.

However, what you pass to Select lamdba is named tuple type:

(string s, char c, int i) tuple) => tuple.i

It appears that type inference does not consider named and unnamed tuples (or two named tuples with different names) completely identical and inference fails in certain cases when such "different" types are used. I'm not completely sure why, maybe it's even a bug, or maybe there is something in specification which covers that.

But, with that knowledge we can figure out that the best way to resolve your problem is to use named tuple type right from the start:

IEnumerable<(string s, char c, int i)> valueTuples = new[] {
    ("First", '1', 1),
    ("Second", '2', 2),
    ("Third", '3', 3)
};            
var projection1 = valueTuples.Select(tuple => tuple.i);

Update: I actually found an issue about this in Roslyn repository. It's a confirmed bug indeed, and it has already been resolved even. There are no explanations about nature of this bug. Fix should appear in version 15.7.

Still even with a fix I consider using named tuple from the start is the best way to go. Then you don't need to specify names again and again in every lambda (and those names can even be different, which makes the whole thing even more confusing).

like image 55
Evk Avatar answered Sep 18 '22 01:09

Evk