以下程序无法编译,因为在出错的行中,编译器选择使用单个T
参数作为分辨率的方法,该方法失败,因为List<T>
不适合通用单T
的约束。编译器无法识别可以使用的其他方法。如果我删除单个 - T
方法,编译器将正确地找到许多对象的方法。
我已经阅读了两篇关于通用方法解析的博客文章,其中一篇来自JonSkeet here,另一篇来自Eric Lippert here,但我无法找到解释或解决问题的方法
显然,使用两个具有不同名称的方法会起作用,但我喜欢这样一个事实:对于这些情况,你只有一种方法。
namespace Test
{
using System.Collections.Generic;
public interface SomeInterface { }
public class SomeImplementation : SomeInterface { }
public static class ExtensionMethods
{
// comment out this line, to make the compiler chose the right method on the line that throws an error below
public static void Method<T>(this T parameter) where T : SomeInterface { }
public static void Method<T>(this IEnumerable<T> parameter) where T : SomeInterface { }
}
class Program
{
static void Main()
{
var instance = new SomeImplementation();
var instances = new List<SomeImplementation>();
// works
instance.Method();
// Error 1 The type 'System.Collections.Generic.List<Test.SomeImplementation>'
// cannot be used as type parameter 'T' in the generic type or method
// 'Test.ExtensionMethods.Method<T>(T)'. There is no implicit reference conversion
// from 'System.Collections.Generic.List<Test.SomeImplementation>' to 'Test.SomeInterface'.
instances.Method();
// works
(instances as IEnumerable<SomeImplementation>).Method();
}
}
}
答案 0 :(得分:6)
方法解析表明closer is better。有关准确的规则,请参阅博客文章。
近点是什么意思?编译器将查看是否可以找到完全匹配,如果由于某种原因它找不到下一个可能的兼容方法等等。
让我们首先通过删除SomeInterface
约束来编译该方法。
public static class ExtensionMethods
{
public static void Method<T>(this T parameter) //where T : SomeInterface
{ }
public static void Method<T>(this IEnumerable<T> parameter) //where T : SomeInterface
{ }
}
现在编译器很乐意编译,并注意两个方法调用都转到Method(T)
而不是Method(IEnumerable<T>)
。那是为什么?
因为Method(T)
更接近于可以将任何类型作为参数,并且它不需要任何转换。
为什么
Method(IEnumerable<T>)
不会更近?
这是因为您将变量的编译时类型设置为List<T>
,因此需要从List<T>
到IEnumerable<T>
的引用转换。哪个更接近,但根本没有做任何转换。
回到你的问题。
为什么
instances.Method();
无法编译?
同样,如前所述使用Method(IEnumerable<T>)
我们需要一些参考转换,所以显然不会更接近。现在我们只剩下一种非常接近的方法Method<T>
。但问题是您已使用SomeInterface
限制它,并且List<SomeImplementation>()
显然无法转换为SomeInterface
。
问题是(猜测)在编译器选择更近的重载之后检查通用约束。在这种情况下,这会使选择的最佳重载无效。
您可以通过将变量的静态类型更改为可用的IEnumerable<SomeImplementation>
来轻松修复它,现在您知道原因了。
IEnumerable<SomeImplementation> instances = new List<SomeImplementation>();
答案 1 :(得分:1)
您是否尝试过在没有泛型的情况下实现第一个,因为它的行为应该相同:
public static void Method(this SomeInterface parameter) { /*...*/ }
或者,正如德米特里建议的那样,通过以下方式呼叫第二个:
instances.Method<SomeImplementation>();
但是,您需要在每次通话中添加<SomeImplementation>
...
答案 2 :(得分:1)
虽然我知道你不想要它,但我认为你应该重新思考方法名称是否应该相同。我看不出同一个名字如何对一个实例和这些实例的集合起作用。例如,如果您的方法名称Shoot
为T
,那么另一种方法应该听起来像ShootThemAll
或类似的东西。
否则你应该让你的作业略有不同:
IEnumerable<SomeImplementation> instances = new List<SomeImplementation>();
instances.Method(); //now this should work
作为最后一个选项,正如Dimitry在评论中所说,你必须明确指定类型参数。
instances.Method<SomeImplementation>();