如何在匿名方法中使用ValueTuple命名特性?

5
我想使用ValueTuple的命名功能,如下所示:
     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);

但是它编译时会出现一条不太有用的错误信息。然而这两个都可以编译:

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

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

尝试更直接的方法,这种方式虽然无法编译,但会给出更有帮助的错误信息提示:
     var projection4 = Enumerable.Select(
        valueTuples, ((string s, char c, int i) tuple) => tuple.i);

这导致尝试编译以下内容:

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

最终启发了这个编译器,它可以将以下内容编译:
     var projection6 = valueTuples.Select<(string, char, int), int>(
        ((string s, char c, int i) tuple) => tuple.i);

这是关于IEnumerable扩展方法的普遍问题吗? 看起来并不是,因为下面的代码可以编译通过:

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

为什么我要使用projection6语法才能使它工作?

1个回答

7
你有一个无名称的元组列表 (string, char, int)(注意没有名称)。因此,使用默认名称 Item1Item2 等访问是可以正常工作的。
然而,你传递给 Select lambda 的是命名元组类型:
(string s, char c, int i) tuple) => tuple.i

似乎类型推断并不认为命名元组和未命名元组(或者两个命名元组有不同的名称)是完全相同的,因此在使用这些“不同”的类型时可能会出现推断失败的情况。我不确定原因是什么,也许这甚至是一个bug,或者规范中有一些内容涵盖了这一点。
但是,有了这个知识,我们可以发现解决问题的最佳方法是从一开始就使用命名元组类型:
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);

更新:我在Roslyn存储库中找到了一个关于这个问题的问题。这确实是一个已确认的错误,甚至已经得到了解决。关于这个错误的性质没有任何解释。修复应该会出现在15.7版本中。

即使有修复,我认为从一开始使用命名元组仍然是最好的方法。然后你就不需要在每个lambda表达式中再次指定名称(这些名称甚至可以不同,这使整个过程更加混乱)。


值得注意的是,这不是特定于IEnumerable、Linq或扩展方法的问题,只需使用简单的通用方法即可轻松重现。有关类型推断的C#语言规范可以在此处找到:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#type-inference - Peter Aylett
是的,谢谢。我错过了那个简单的解决方案 - 可能是因为我的实际IEnumerable是从另一个方法返回的。但我可以访问该方法并在返回签名中给ValueTuple命名,这样就可以工作了。我开始喜欢ValueTuples了。 - mikevg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接