让我们假设以下示例性问题 - 我希望创建一个方法,只输出任意List<>
集合中的元素数量。
我用一个方法创建了以下静态类:
public static class MyClass
{
public static void MyMethod<T>(T obj) where T : List<int> // sort of pointless, yes
{
Console.WriteLine(obj.Count);
}
}
请注意T
是List<int>
的子类。现在,我可以致电:
List<int> li = new List<int>();
MyClass.MyMethod<List<int>>(li);
现在,IDE告诉我&#34;类型参数规范是多余的&#34;。它可以根据用途推断出类型:
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);
}
现在,在不明确描述类型的情况下调用此方法会产生错误&#34;方法的类型参数无法从使用中推断出来&#34;:
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
约束。
答案 0 :(得分:2)
您应该考虑您对该类型的实际要求。
在你的情况下,你想做什么?您希望能够在您在方法中创建的客户端上执行action
。该客户端是您作为泛型类型参数传递的类型。您是否需要知道它是ClientBase<something>
才能执行操作?否。
你还对这个对象做了什么?您可以打开和关闭频道。这些是ICommunicationObject
ClientBase<T>
实现的行动。
这就是你的全部要求。所以你想要有以下约束:
ICommunicationObject
的类型,以便您可以打开/关闭频道。所以你的方法看起来像这样:
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>()
,那么它将是不明确的。
至于为什么编译器无法在你的情况下推断所有类型的参数,这只是因为约束给出的类型参数之间的关系不会被推断出类型推断。