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

本文关键字:语法 类型 存在 是否 | 更新日期: 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本身并不是类型参数 -但即使SomeClassT中是泛型的,这也会失败。

在许多情况下,与此类似,您可以通过非泛型类型中的额外静态方法,通过类型推断来创建泛型类型的实例-但具体情况通常是您有两个类型参数,并且您希望显式指定其中的一个

需要注意的重要一点是,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#类型推断的能力是有限的,这就是其中之一。如果不显式地指定类型,就无法执行请求。