强制调用正确的方法重载

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&lt;string&gt; 你能把第一个重载改成接受object而不是泛型吗? 【参考方案1】:

您可以在初始化后首先将其显式转换为IEnumerable&lt;T&gt; 引用,然后将其作为参数传递给方法,如下所示,以便它解析为更具体的重载,即IEnumerable&lt;T&gt; 而不是T:

DoSomething(new[]  "Pizza", "Chicken", "Cheese"  as IEnumerable<string>);

或者也可以这样做:

IEnumerable<string> foods = new[]  "Pizza", "Chicken", "Cheese" ;
DoSomething(foods);

问题是当你不将它转换为IEnumerable&lt;T&gt;时,它在编译时的类型是String[],所以它会解决以T作为输入参数的重载。

所以在调用时,您的代码实际上是这样编译的:

DoSomething<String[]>(foods);

所以它调用第一个重载而不是第二个。

另一种解决方案是自己显式指定泛型类型参数,而不是编译器自行解析,如下所示:

DoSomething<String>(foods);

请参考我刚刚创建的以下演示小提琴来解释它:

Demo Fiddle

编辑:

正如一些人建议的那样,最好将方法名称更改为足够明显,以便人们可以理解它可以处理哪种类型,尽管我提到的解决方法会起作用。

希望对你有帮助!

【讨论】:

虽然这确实有效,但 OP 最好更改方法名称以避免以后混淆。 是的,你是对的,方法名称应该清楚它的用途 或者只是DoSomething&lt;string&gt;(new[] "Pizza") 是的,你完全正确@CamiloTerevinto 更新了帖子【参考方案2】:

当一个方法有多个适用的重载时,有几个不同的因素可用于确定哪个“更好”。其中一个因素是每个参数与方法签名中的类型有多“接近”。 string[]IEnumerable&lt;string&gt;,所以它在那个参数槽中有效,但 string[] 更接近 string[](完全匹配为“关闭”因为它得到)而不是IEnumerable&lt;string&gt;,所以第一个重载比第二个“更接近”。

因此,如果您将第二个参数的编译时间类型更改为完全IEnumerable,那么两个重载在“接近度”范围内将完全相同。既然是一样的,它就会进入另一个“更好”的决胜局,即非泛型方法比泛型方法“更好”,所以你最终会调用你想要的重载。

至于如何将该表达式的编译时类型更改为IEnumerable&lt;string&gt;,最方便的方法是AsEnumerable

DoSomething(new[]  "Pizza", "Chicken", "Cheese" .AsEnumerable());

【讨论】:

或者只是DoSomething&lt;string&gt;(new[] "Pizza")【参考方案3】:

正如@Servy 的优秀答案中所解释的,编译器将为重载方法选择最佳匹配类型。

通过将通用函数改为使用 object,您可以使任何 IEnumerable&lt;&gt; 与该函数最匹配。

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关键字

在C#中,关于重载方法的说法正确的是(D)。 (选择一项)

多态与重载

python重载方法

方法重载和继承——如何找到正确的方法?

Atitit.编程语言原理---方法重载的实现与设计 调用方法的原理