2012-07-20 55 views
9

我不认为我做了太深奥的任何事情,但我没有看到任何其他的问题。我有一个最大公分母(“A类”),它是一个最大的公分母(“A类”),它是一个最大公约号(“A”),它是一个最大公约号也在方法“Frob”的返回类型中明确定义。编译器不应该在lambda表达式中创建所有返回类型的列表,创建一个祖先树以找到它们的共同祖先,然后将其与包含方法的预期返回类型进行协调?C#类型参数不能从select中的用法推断多个返回

方法'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable,System.Func)'的类型参数不能从用法中推断出来。尝试明确指定类型参数。

using System.Collections.Generic; 
using System.Linq; 
using System.Text.RegularExpressions; 

namespace Sample 
{ 
    public abstract class A 
    { 
     private A(int index) { /* ... */ } 

     public sealed class A1 : A 
     { 
      public A1(string text, int index) 
       : base(index) 
      { /* ... */ } 
     } 

     public sealed class A2 : A 
     { 
      public A2(int index) 
       : base(index) 
      { /* ... */ } 
     } 

     private static Regex _regex = new Regex(@"(to be)|(not to be)"); 
     public static IEnumerable<A> Frob(string frobbable) 
     { 
      return _regex.Matches(frobbable) 
       .Cast<Match>() 
       .Select((match, i) => 
       { 
        if (match.Groups[1].Success) 
        { 
         return new A1(match.Groups[1].Value, i); 
        } 
        else 
        { 
         return new A2(i); 
        } 
       }); 
     } 
    } 
} 
+2

你有没有尝试在返回之前将A1和A2投射到A?但是,是的,我可以看到为什么编译器会抱怨,它不会奇迹般地知道您希望共同分母是A级。 – 2012-07-20 22:08:00

回答

16

这是C#4规范的节7.5.2.12:

匿名函数的推断返回类型F用于类型推断和重载分辨率。推断的返回类型只能针对所有参数类型已知的匿名函数确定,或者是因为它们是通过匿名函数转换显式给出的,或者是在封闭泛型方法调用的类型推断期间推断的。推断的返回类型确定如下:

  • 如果F的主体是表达式,则F的推断返回类型是该表达式的类型。
  • 若F的主体是一个块,并在该块的return语句的表达式集具有最好的通用类型T(§7.5.2.14),则F的推断的返回类型为T.
  • 否则,返回类型不能推断为E.

第7.5.2节。14是这样的:

在某些情况下,需要为一组表达式推断出一个通用类型。特别是,隐式类型数组的元素类型和块体的匿名函数的返回类型可以通过这种方式找到。

直观地看,给定一组表达式E1 ... EM这个推断应该是相当于调用一个方法

Tr M<X>(X x1 … X xm) 

与荣作为参数。

更准确地说,推理从一个不固定的类型变量X开始。然后从每个Ei到X产生输出类型推论。最后,X是固定的,如果成功,则得到的类型S是所得到的最佳通用类型表达式。如果不存在这样的S,则表达式不具有最佳通用类型。

所以,假设我们有:

void M<X>(X x1, Xx2) {} 

A1 a1 = new A1(); 
A2 a2 = new A2(); 
M(a1, a2); 

...这将无法确定X一个类型参数,所以返回值推断失败同样的方式。

我怀疑,如果你将或者的返回值转换为A,它就会起作用。

+0

啊,这是C#规范。感谢您发布Jon。 – 2012-07-20 22:19:24

+1

非常真实,铸造任何一个到“A”确实有效。 – 2012-07-20 22:30:16

+0

所以基本上规范是说推理过程在两种情况下都是相同的。我理解了。但它并没有真正解释“修复X”的过程是什么 - 这是真正的问题,编译器无法做出决定。为什么不能选择最具体的可能类型,如果它知道不会有任何一种方式(“对象)”或其他(“A)”的副作用? – 2012-07-20 22:32:26

6

我猜有一个特定的C#规范条款的地方,决定这一点。 (编辑:乔恩Skeet发现它,并张贴在他的答案)

通常,这种lambda(或三元操作等)需要在每个阶段具有相同的确切返回类型,以避免歧义。例如,在你的情况下,你想返回类型AObject?当您将界面或多层次的继承添加到组合中时更有趣。

在这种情况下

最好的办法是简单地投下每个return语句的输入A或将其存储在一个临时变量:

if (match.Groups[1].Success) 
    return (A)(new A1(match.Groups[1].Value, i)); 
else 
    return (A)(new A2(i)); 

A returnValue; 

if (match.Groups[1].Success) 
    returnValue = new A1(match.Groups[1].Value, i); 
else 
    returnValue = new A2(i); 

return returnValue; 

编辑:如果你是没有推断类型,您可以明确地调用Select查询:

.Cast<Match>() 
.Select<Match, A>((match, i) => 
{ 
    if (match.Groups[1].Success) 
     return new A1(match.Groups[1].Value, i); 
    else 
     return new A2(i); 
}); 

那么编译器将只需要确保你的返回类型与A隐含兼容(他们是)

+0

不;返回类型不需要是固定的。 – SLaks 2012-07-20 22:17:05

+1

@SLaks我认为他们所做的类型推断。如果Lars明确地输入了“Select ”,那么它们可以是任何可以被隐式视为类型“A”的对象,而无需明确地投射它们。 – 2012-07-20 22:24:16

+0

感谢您的帮助 - 但这只是解决方法,它不是“为什么”我不得不做什么似乎是不必要的工作的答案。我更感兴趣的是“为什么编译器不能聪明”这个方面。 – 2012-07-20 22:24:42

相关问题