强制调用正确的方法重载
Posted
技术标签:
【中文标题】强制调用正确的方法重载【英文标题】:Enforce right method overload to be called 【发布时间】:2018-06-21 04:08:19 【问题描述】:我有两种带有以下签名的方法:
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 ...
【问题讨论】:
将第二个重命名为DoSomethingToACollection
我已经考虑过了,但我仍然想保持名称不变。我也不太明白,为什么会编译。
在生活中,我们不可能总是拥有我们想要的一切。您的T value
参数将捕获所有内容,因为所有内容都是T
。您不希望两种不同的方法具有相同的名称,一种显式处理IEnumerable
,另一种显式处理T
,省得您头疼,只需更改名称即可。
调用方法时可以显式指定泛型类型DoSomething<string>
。
你能把第一个重载改成接受object
而不是泛型吗?
【参考方案1】:
您可以在初始化后首先将其显式转换为IEnumerable<T>
引用,然后将其作为参数传递给方法,如下所示,以便它解析为更具体的重载,即IEnumerable<T>
而不是T
:
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);
请参考我刚刚创建的以下演示小提琴来解释它:
Demo Fiddle
编辑:
正如一些人建议的那样,最好将方法名称更改为足够明显,以便人们可以理解它可以处理哪种类型,尽管我提到的解决方法会起作用。
希望对你有帮助!
【讨论】:
虽然这确实有效,但 OP 最好更改方法名称以避免以后混淆。 是的,你是对的,方法名称应该清楚它的用途 或者只是DoSomething<string>(new[] "Pizza")
是的,你完全正确@CamiloTerevinto 更新了帖子【参考方案2】:
当一个方法有多个适用的重载时,有几个不同的因素可用于确定哪个“更好”。其中一个因素是每个参数与方法签名中的类型有多“接近”。 string[]
是 IEnumerable<string>
,所以它在那个参数槽中有效,但 string[]
更接近 string[]
(完全匹配为“关闭”因为它得到)而不是IEnumerable<string>
,所以第一个重载比第二个“更接近”。
因此,如果您将第二个参数的编译时间类型更改为完全IEnumerable
,那么两个重载在“接近度”范围内将完全相同。既然是一样的,它就会进入另一个“更好”的决胜局,即非泛型方法比泛型方法“更好”,所以你最终会调用你想要的重载。
至于如何将该表达式的编译时类型更改为IEnumerable<string>
,最方便的方法是AsEnumerable
:
DoSomething(new[] "Pizza", "Chicken", "Cheese" .AsEnumerable());
【讨论】:
或者只是DoSomething<string>(new[] "Pizza")
【参考方案3】:
正如@Servy 的优秀答案中所解释的,编译器将为重载方法选择最佳匹配类型。
通过将通用函数改为使用 object
,您可以使任何 IEnumerable<>
与该函数最匹配。
public void DoSomething(object value) ...
public void DoSomething<T>(IEnumerable<T> value) ...
我认为理论上可能将value
向下转换为object
可能会导致与泛型方法相比的性能问题,但由于DoSomething
不返回value
或采用另一个匹配类型@ 的参数987654328@,object
的使用仅限于 DoSomething
的主体,并且将允许 T
的所有合法使用。
【讨论】:
【参考方案4】:因为T
接受所有类型,而string[]
不是直接的IEnumerable
。它是从它衍生出来的。
DoSomething(new[] "Pizza", "Chicken", "Cheese" .AsEnumerable());
【讨论】:
"string[]
不是IEnumerable
。"确实是。如果不是,那么您的代码将无法编译。
typeof(object[]).GetInterfaces().Contains(typeof(IEnumerable)) == true
@Jonathon Chase,当然是真的。 Array 无论如何都实现了 IEnumerable。以上是关于强制调用正确的方法重载的主要内容,如果未能解决你的问题,请参考以下文章
面向对象的过程继承封装多态;抽象类访问修饰符的使用引用类型强制转换方法重写@override与重载空指针异常super关键字