c# - 为什么不能为泛型类型推断类型参数?

标签 c# .net generics inheritance

默认情况

让我们假设以下示例性问题 - 我想要创建一个方法,该方法将简单地输出任何 List<> 中的元素数量。收藏.
我用一种方法创建了以下静态类:

public static class MyClass
{
    public static void MyMethod<T>(T obj) where T : List<int> // sort of pointless, yes
    {
        Console.WriteLine(obj.Count);
    }
}    

请注意 TList<int> 的子类.现在,我可以调用:

List<int> li = new List<int>();
MyClass.MyMethod<List<int>>(li);

现在,IDE 告诉我“类型参数规范是多余的”。它可以从用法中推断出类型:

List<int> li = new List<int>();
MyClass.MyMethod(li); // OK. li is List<int>, type argument is not required

一般情况

据你所知,我想输出 List 的计数任何类型。像这样的东西会很棒:

public static void MyComplexMethod<T>(T obj) where T : List<any>
{
    Console.WriteLine(obj.Count);
}

但是,这是一个不正确的语法。我必须实现以下方法:

public static void MyComplexMethod<T1, T2>(T1 obj) where T1 : List<T2>
{
    Console.WriteLine(obj.Count);
}

现在,在不明确描述类型的情况下调用此方法会产生错误“无法从用法中推断出方法的类型参数”:

List<int> li = new List<int>();
MyClass.MyComplexMethod(li); // error
MyClass.MyComplexMethod<List<int>>(li); // error
MyClass.MyComplexMethod<List<int>, int>(li); // OK

MyClass.MyComplexMethod<List<double>, double>(new List<double>()); // OK
MyClass.MyComplexMethod<List<string>, string>(new List<string>()); // OK

// error. The type must be convertible in order to use...So, compiler knows it
MyClass.MyComplexMethod<List<string>, double>(new List<string>()); 

但是,对我来说,类型似乎应该可以从用法中推断出来。我提供List<int> - T1 是 List<int> T2 是 int , 明显地。为什么编译器不能这样做?实现理想行为的最合理方法是什么 (where T : List<any>)?

真实案例

如果有人只是想知道我为什么需要这个。实际上,当我尝试实现 WCF 代理包装器时,我偶然发现了这种情况,如下所示:

public static void Call<TServiceProxy, TServiceContract>(Action<TServiceProxy> action)
    where TServiceProxy : ClientBase<TServiceContract>, new()
    where TServiceContract : class
{
    TServiceProxy serviceProxy = new TServiceProxy();
    try
    {
        action(serviceProxy);
        serviceProxy.Close();
    }
    catch (Exception ex)
    {
        serviceProxy.Abort();
        // Log(ex);
        throw;
    }
}

Service.Call<EchoServiceClient>(x => {
    int v = DateTime.Now.ToString();
    x.Echo(v);
}); // not working

Service.Call<EchoServiceClient, IEchoService>(x => {
    int v = DateTime.Now.ToString();
    x.Echo(v);
}); // not convenient, pointless. EchoServiceClient inherits from ClientBase<IEchoService>

没有where TServiceProxy : ClientBase<TServiceContract>我做不到 serviceProxy.Abort() .同样,where TServiceProxy : ClientBase<any>将是一个很好的解决方案,因为实际上 TServiceContract没关系 - 它仅用于 where约束条件。

最佳答案

您应该考虑您对该类型的实际要求是什么。

在你的情况下,你想做什么?您希望能够执行 action在您在该方法中创建的客户端上。该客户端是您作为泛型类型参数传递的类型。你需要知道这是一个ClientBase<something>吗?为了执行 Action ?没有。

你还对这个对象做了什么?您打开和关闭 channel 。这些是由 ICommunicationObject 确保的操作哪个ClientBase<T>实现。

这就是您的全部要求。所以你想有以下约束:

  • 能够创建该类型的对象。
  • 实现类型 ICommunicationObject这样您就可以打开/关闭 channel 。

因此您的方法可能如下所示:

public static void Call<T>(Action<T> action)
    where T: ICommunicationObject, new()
{
    T serviceProxy = new T();
    try
    {
        action(serviceProxy);
        serviceProxy.Close();
    }
    catch (Exception ex)
    {
        serviceProxy.Abort();
        throw;
    }
}

最后,回答你为什么编译器不能自动解决这个问题:如果你有泛型类型参数,有两种可能性。要么编译器能够推断出所有 类型参数,在这种情况下您可以将它们省略,要么编译器无法推断出所有参数,在这种情况下您需要全部指定它们。毕竟Foo<X>()Foo<X, Y>()是不同的方法签名,所以如果后者也允许 Foo<X>() , 这将是模棱两可的。

至于为什么编译器不能在您的情况下推断出所有 类型参数,这仅仅是因为没有为类型推断评估约束给定的类型参数之间的关系。

关于c# - 为什么不能为泛型类型推断类型参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33255841/

相关文章:

c# - C# 4.0 中的命名参数和泛型类型推断

.net - 不需要取消的异步方法

c# - 类型 '' 不能用作泛型类型或方法 'T' 中的类型参数 ''。没有从 '' 到 '' 的隐式引用转换

c# - 从继承自泛型接口(interface)的类中获取泛型参数的类型

c# - 如何使用用于 XML 序列化的公共(public)访问器指定非序列化字段

c# - 在 Xamarin Forms 中的应用程序之间共享数据

c# - 获取从A*出发的最优路径

c# - 从自定义属性装饰属性中获取值(value)?

c# - 连接的主机未能响应,超时已过

java - 对 List<Generic> 显式强制转换的怀疑