是否存在反向类型推断的语法?

时间:2013-03-25 15:45:28

标签: c# generics types type-inference

当我试图回答这个问题时:

Is it possible to get rid of the TClient generic type in the Service class

我发现了一个奇怪的用法,我从来没有设计过这种无法编译的语法,以下是我遇到的代表:

interface IGeneric<T> {
}

partial class SomeClass {
    // won't compile
    public static void SomeMethod<U>(Action<T> d) where T: IGeneric<U> {
    }
}

即使宣布为:

class Concrete: IGeneric<object> {
}

partial class SomeClass {
    public static void SomeMethod<U>(Action<IGeneric<U>> d) { // compiles
    }
}

使以下代码可编译:

var d=default(Action<Concrete>);
SomeClass.SomeMethod(d); // won't compile

我不知道一个语法是否有效没有两个类型参数。

所以我想知道这种反向类型推断是否存在语法?还是一个解决方法?

2 个答案:

答案 0 :(得分:4)

简单的答案是否定的。这最初不是关于类型推理 - 它是关于类型约束。您只能添加约束类型参数,该参数在同一声明中引入。所以这个:

public static void SomeMethod<U>(Action<T> d) where T: IGeneric<U>

无效,因为您尝试以T为限制U,而U实际上是在方法声明中引入的T。实际上,SomeClass本身并不是任何地方的类型参数 - 但即使T中的Action<Concrete>是通用的,这也会失败。

在很多情况下类似,你可以通过非泛型类型的额外静态方法,通过类型推断创建泛型类型的实例 - 但具体通常是你有两个类型参数,你想明确指定一个

需要注意的一点是,Action<IGeneric<object>>只是而不是 Concrete。例如,Action<Concrete>可能会暴露Action<IGeneric<object>>可能依赖的一些额外属性 - 但是如果给出IGeneric<object>,您可以使用不同的实现来轻松调用SomeMethod {1}}。您现有的Action<U>尝试按特定Action<IGeneric<T>>而不是U对其进行排序 - 但此时使用操作相对较难。即使在类型推断有效的情况下,这很少(根据我的经验)是一种实用的方法。

一旦你改为一个真正协变的代表(假设你正在使用C#4),那么除非你关心using System; interface IGeneric<T> {} class SomeClass { public static void SomeMethod<T>(Func<IGeneric<T>> d) {} } class Concrete: IGeneric<object> {} class Test { static void Main() { var d = default(Func<Concrete>); // This compiles fine SomeClass.SomeMethod(d); } } ,否则你可以简单地使用不同的签名:

{{1}}

答案 1 :(得分:2)

问题是你试图将Action<T>视为T中的协变,但事实并非如此。事实上,它是逆变的。

例如,如果你有一个协变代表,就像这样。

delegate T CovariantCall<out T>();

你可以轻松地做你想要的事。

CovariantCall<IGeneric<object>> covariant = default(CovariantCall<Concrete>);

您的第一个示例未编译,因为您在方法的声明的类型参数列表中省略了T。不过,这是一个更好的主意,并且它的工作原理是因为约束只验证并且不影响参数方差,但是您必须明确指定您要查找的参数并且无法推断它。

public static void SomeMethod<T, U>(Action<T> d) where T: IGeneric<U>
{ 
    ...
}

SomeClass.SomeMethod<Concrete, object>(default(Action<Concrete>));

C#类型推断能够有限制,这就是其中之一。如果没有明确指定类型,你就不能做你想要的。