无法从带有多个返回的 Select 中的使用推断 C# 类型参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11587536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# type arguments cannot be inferred from usage in Select with multiple returns
提问by Lars Kemmann
I don't think I'm doing anything too esoteric, but I don't see any other questions about this.
我不认为我在做任何太深奥的事情,但我没有看到关于此的任何其他问题。
The following code (I've reduced it to the essentials) generates a compiler error in C# 4. However, it should be obvious what the type argument is - there's a greatest common denominator ("class A") that is also explicitly defined in the return type of the method "Frob". Shouldn't the compiler make a list of all the return types in the lambda expression, create an ancestry tree to find their common ancestors, and then reconcile that with the expected return type of the containing method?
以下代码(我已将其简化为基本内容)在 C# 4 中生成编译器错误。但是,类型参数是什么应该很明显 - 有一个最大的公分母(“类 A”),它也在 C# 4 中显式定义方法“Frob”的返回类型。编译器不应该列出 lambda 表达式中的所有返回类型,创建一个祖先树来查找它们的共同祖先,然后将其与包含方法的预期返回类型进行协调吗?
The type arguments for method 'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
无法从用法推断方法“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);
}
});
}
}
}
采纳答案by Jon Skeet
It's section 7.5.2.12 of the C# 4 spec:
这是 C# 4 规范的第 7.5.2.12 节:
The inferred return type of an anonymous function F is used during type inference and overload resolution. The inferred return type can only be determined for an anonymous function where all parameter types are known, either because they are explicitly given, provided through an anonymous function conversion or inferred during type inference on an enclosing generic method invocation. The inferred return type is determined as follows:
- If the body of F is an expression, then the inferred return type of F is the type of that expression.
- If the body of F is a block and the set of expressions in the block's return statements has a best common type T (§7.5.2.14), then the inferred return type of F is T.
- Otherwise, a return type cannot be inferred for E.
在类型推断和重载解析期间使用匿名函数 F 的推断返回类型。推断的返回类型只能为所有参数类型都已知的匿名函数确定,要么是因为它们是显式给定的、通过匿名函数转换提供的,要么是在封闭泛型方法调用的类型推断期间推断出来的。推断的返回类型确定如下:
- 如果 F 的主体是一个表达式,则 F 的推断返回类型是该表达式的类型。
- 如果 F 的主体是一个块,并且块的 return 语句中的表达式集具有最佳公共类型 T(第 7.5.2.14 节),则 F 的推断返回类型为 T。
- 否则,无法为 E 推断返回类型。
Section 7.5.2.14 is this:
第 7.5.2.14 节是这样的:
In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and the return types of anonymous functions with block bodies are found in this way.
Intuitively, given a set of expressions E1…Em this inference should be equivalent to calling a method
Tr M<X>(X x1 … X xm)with the Ei as arguments.
More precisely, the inference starts out with an unfixed type variable X. Output type inferences are then made from each Ei to X. Finally, X is fixed and, if successful, the resulting type S is the resulting best common type for the expressions. If no such S exists, the expressions have no best common type.
在某些情况下,需要为一组表达式推断一个公共类型。特别是隐式类型数组的元素类型和带有块体的匿名函数的返回类型都是通过这种方式找到的。
直觉上,给定一组表达式 E1…Em 这个推断应该相当于调用一个方法
Tr M<X>(X x1 … X xm)以 Ei 作为参数。
更准确地说,推断从一个不固定的类型变量 X 开始。然后从每个 Ei 到 X 进行输出类型推断。最后,X 是固定的,如果成功,结果类型 S 是表达式的结果最佳公共类型。如果不存在这样的 S,则表达式没有最佳公共类型。
So, suppose we have:
所以,假设我们有:
void M<X>(X x1, X x2) {}
A1 a1 = new A1();
A2 a2 = new A2();
M(a1, a2);
... that would fail to determine a type argument for X, so return value inference fails in the same way.
...这将无法确定 的类型参数X,因此返回值推断以相同的方式失败。
I suspect that if you cast eitherof your return values to A, it will work.
我怀疑如果您将任何一个返回值转换为A,它都会起作用。
回答by Chris Sinclair
I'm guessing there's a specific C# specification clause somewhere that dictates this. (EDIT: Jon Skeet found it and posted them in his answer)
我猜在某处有一个特定的 C# 规范条款规定了这一点。(编辑:Jon Skeet 找到并贴在他的回答中)
Usually, such lambdas (or ternary operations, etc.) require to have the same exact return type at each stage to avoid ambiguity. For example, in your case, did you want to return type Aor Object? Even more fun when you throw interfaces or multiple levels of inheritance into the mix.
通常,此类 lambda(或三元运算等)需要在每个阶段具有相同的确切返回类型以避免歧义。例如,在您的情况下,您想返回 typeA还是Object? 当您将接口或多级继承混合在一起时,会更加有趣。
Best bet in this case is to simply cast each of your return statements to type Aor store it in a temporary variable:
在这种情况下,最好的办法是简单地将每个 return 语句转换为类型A或将其存储在临时变量中:
if (match.Groups[1].Success)
return (A)(new A1(match.Groups[1].Value, i));
else
return (A)(new A2(i));
or
或者
A returnValue;
if (match.Groups[1].Success)
returnValue = new A1(match.Groups[1].Value, i);
else
returnValue = new A2(i);
return returnValue;
EDIT: If you're ok without the inferred type, you can explicitly call the Selectquery with:
编辑:如果没有推断类型就可以了,您可以使用以下命令显式调用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);
});
Then the compiler will just make sure your return types are implicitly compatible with A(which they are)
然后编译器将确保您的返回类型隐式兼容A(它们是)

