我有这段代码(为了清晰起见,最小化):
interface IEither<out TL, out TR> {
}
class Left<TL, TR> : IEither<TL, TR> {
public Left(TL value) { }
}
static class Either {
public static IEither<TL, TR> Left<TL, TR> (this TL left) {
return new Left<TL, TR> (left);
}
}
为什么我不能说:
static class Foo
{
public static IEither<string, int> Bar ()
{
//return "Hello".Left (); // Doesn't compile
return "Hello".Left<string, int> (); // Compiles
}
}
我收到一条错误,指出'string' does not contain a definition for 'Left' and no extension method 'Left' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) (CS1061)
。
答案 0 :(得分:6)
return "Hello".Left<string, int> (); // Compiles
毫不奇怪。您明确声明了类型参数,编译器很满意。
return "Hello".Left (); // Doesn't compile
也不奇怪,在这种情况下,编译器无法找出TR
的内容。可以推断TL
,因为TL
作为参数left
传递,但TR
不能传递。{/ p>
C#编译器没有假设您的意思,如果意图不明确,它将引发编译器错误。如果编译器认为您可能做错了什么,它会给编译器警告。
答案 1 :(得分:5)
我建议您查看C#
规范中的 7.5.2 部分。
7.5.2类型推断
当调用泛型方法而不指定类型参数时,a 类型推断进程尝试推断调用的类型参数。 类型推断的存在允许更方便的语法 用于调用泛型方法,并允许程序员避免 指定冗余类型信息。
[...]
类型推断作为方法调用(第7.6.5.1节)的绑定时处理的一部分发生,并且发生在调用的重载决策步骤之前[...]
即解决方案在任何类型的重载解决方案完成之前发生,问题是按照你说的方式推断类型甚至没有尝试过,坦白说也不总是可行。
泛型类型参数的类型解析只能通过调用中的参数完成!在你的例子中只有一个string
!它不能从参数推断int
,只能推断在泛型类型解析时未解析的调用上下文。
答案 2 :(得分:1)
免责声明:这个答案涉及猜测。
有关TR
的信息在于,它被用作return
的子表达式,而IEither<Foo, Bar>
可以与TR
匹配,以生成Bar
为return
的信息。
但是存在问题。当编译器具有抽象语法树时,通过逐渐移向树的叶子,更容易从根开始并推断表达式类型,解决重载等。这是最容易做的事情,也是经常做的事情。
您的场景要求编译器完全向后工作 - 从方法调用到构造函数调用<string, int>
的子表达式,然后查阅当前方法的声明(并找出正确的组合是var
,因为任何其他人都无法编译)。乍一看,这很难实现。
但是在C#中有一些优先权:你可以创建一个(更高阶)函数,只返回一个lambda,类型推断将根据声明工作。在声明局部变量时,这种事情也适用于lambdas(你不能将它们分配给{{1}}并要求编译器推断出后续用法中的参数类型。)
那么,为什么他们没有为你所描述的情况实施它,考虑到他们是用lambdas做的呢?