我有两种具有以下签名的方法:
public void DoSomething<T>(T value) { ... }
public void DoSomething<T>(IEnumerable<T> value) { ... }
我试图像这样打电话给第二个人:
DoSomething(new[] { "Pizza", "Chicken", "Cheese" });
它仍然跳进第一个。 如何强制它将进入第二种方法呢?例如,通过使用where子句作为泛型参数?
修改
更确切地说:为什么它不起作用,即使我更具体并将重载更改为类似的东西,这会在尝试调用上面显示的方法时导致编译错误:
public void DoSomething<T>(T value) where T : IComparable { ... }
public void DoSomething<T>(IEnumerable<T> value) where T : IComparable { ... }
答案 0 :(得分:6)
您可以在初始化后首先将其显式地转换为IEnumberable<T>
引用,然后作为参数传递给方法,如下所示,以便它解析为更具体的重载IEnumerable<T>
而不是T
{1}}:
DoSomething(new[] { "Pizza", "Chicken", "Cheese" } as IEnumerable<string>);
或者也可以这样做:
IEnumerable<string> foods = new[] { "Pizza", "Chicken", "Cheese" };
DoSomething(foods);
问题是当你没有把它强制转换为IEnumerable<T>
时,它在编译时的类型是String[]
,所以它会解析为T
的重载输入参数。
因此,在调用时,您的代码实际上是编译的:
DoSomething<String[]>(foods);
因此它调用第一个重载而不是第二个重载。
另一种解决方案是自己明确指定泛型类型参数而不是编译器来解析自身,如:
DoSomething<String>(foods);
请参阅我刚刚创建的以下演示小提琴来解释它:
正如有些人建议更好的方法是将方法名称改为足够明显,以便人们可以理解它可以处理哪种类型,尽管我提到的工作方法可行。
希望它有帮助!
答案 1 :(得分:5)
当方法存在多个适用的重载时,有几个不同的因素用于确定哪个“更好”。其中一个因素是每个参数如何“关闭”到方法签名中的类型。 string[]
是IEnumerable<string>
,因此该参数广告位中的有效,但string[]
更接近到string[]
(完全匹配与IEnumerable<string>
一样“接近”,因此第一次重载比第二次“更接近”。
因此,如果将第二个参数的编译时类型更改为IEnumerable
,那么两个重载在“紧密度”范围内将完全相同。由于它是相同的,它继续到另一个“更好”的决胜局,这是非泛型方法比通用方法“更好”,所以你最终调用你想要的重载。
至于如何将该表达式的编译时类型更改为IEnumerable<string>
,最方便的方法是AsEnumerable
:
DoSomething(new[] { "Pizza", "Chicken", "Cheese" }.AsEnumerable());
答案 2 :(得分:1)
正如@ Servy的优秀答案中所解释的,编译器将为重载方法选择最佳匹配类型。
通过将通用功能更改为使用object
,您可以使IEnumerable<>
的任何内容与该功能最佳匹配。
public void DoSomething(object value) { ... }
public void DoSomething<T>(IEnumerable<T> value) { ... }
我认为理论上可能向下转换value
到object
会导致性能问题而不是泛型方法,但由于DoSomething
没有返回value
或者使用匹配类型T
的其他参数,object
的使用仅限于DoSomething
的正文,并允许T
合法使用。
答案 3 :(得分:0)
因为T
接受所有类型而string[]
不是IEnumerable
直接。它源于它。
DoSomething(new[] { "Pizza", "Chicken", "Cheese" }.AsEnumerable());