是否存在反向类型推断的语法
本文关键字:语法 类型 存在 是否 | 更新日期: 2023-09-27 18:05:51
当我试图回答这个问题时:
是否可以在Service类中去掉TClient泛型类型
我发现了一个奇怪的用法,我从来没有设计过这种不可编译的语法,下面是我遇到的一个代表:
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
}
}
将not使以下代码可编译:
var d=default(Action<Concrete>);
SomeClass.SomeMethod(d); // won't compile
我不知道在没有两个类型参数的情况下工作的语法。
所以我想知道是否有一种语法可以做这种反向类型推断?还是一种变通方法?
答案是否定的。这甚至不是关于类型推断,而是关于类型约束。您只能添加约束在同一声明中引入的类型参数。所以这:
public static void SomeMethod<U>(Action<T> d) where T: IGeneric<U>
是无效的,因为你试图用U
来约束T
,而实际上是在方法声明中引入的U
。事实上,T
本身并不是类型参数 -但即使SomeClass
在T
中是泛型的,这也会失败。
在许多情况下,与此类似,您可以通过非泛型类型中的额外静态方法,通过类型推断来创建泛型类型的实例-但具体情况通常是您有两个类型参数,并且您希望显式指定其中的一个。
需要注意的重要一点是,Action<Concrete>
只是而不是和Action<IGeneric<object>>
。例如,Concrete
可能会暴露一些Action<Concrete>
可能依赖的额外属性-但是给定Action<IGeneric<object>>
,您可以轻松地使用IGeneric<object>
的不同的实现来调用它。您现有的SomeMethod
试图通过特定的Action<U>
而不是Action<IGeneric<T>>
来解决这一问题-但在这一点上,使用操作相对困难。这很少(以我的经验)是一种实用的方法,即使类型推断有效。
一旦您更改为真正的协变委托(假设您使用c# 4),那么除非您关心U
,否则您可以简单地使用不同的签名:
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);
}
}
问题是您试图将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#类型推断的能力是有限的,这就是其中之一。如果不显式地指定类型,就无法执行请求。