【问题标题】:Generic and non-generic method overload rules with type inference具有类型推断的泛型和非泛型方法重载规则
【发布时间】:2014-08-18 18:57:46
【问题描述】:

给定以下两个重载方法签名:

public B DoSomething<A,B>(A objOne, B objTwo)
public object DoSomething(object objOne, Type objType);

我希望调用它会调用第二个签名,但它不会:

var obj = new SomeType();
var type = typeof(SomeOtherType);

DoSomething(obj, type);

在我看来,查看规范,第二个重载更适用(具有一种确切的类型和较少的数量)。但是,它不会被调用。相反,调用第一个重载时,A 为 object 类型,B 为 Type 类型。为什么会这样?除了重命名方法或使用命名参数之外,还有其他方法可以调用此方法吗?

编辑:

以下是我在 7.5.3.2 中引用的规范部分:

如果参数类型序列 {P1, P2, ..., PN} 和 {Q1, Q2, ..., QN} 等价(即每个 Pi 有一个到相应 Qi 的恒等转换),以下平局规则应用,以便确定更好的功能成员。

  • 如果 MP 是非泛型方法而 MQ 是泛型方法,则 MP 优于 MQ。
  • ...
  • 否则,如果 MP 比 MQ 有更具体的参数类型,则 MP 优于 MQ。令 {R1, R2, ..., RN} 和 {S1, S2, ..., SN} 表示 MP 和 MQ 的未实例化和未扩展的参数类型。 MP 的参数类型比 MQ 的更具体,如果对于每个参数,RX 不低于 SX,并且,对于至少一个参数,RX 比 SX 更具体:
    • 类型参数不如非类型参数具体。
    • ...

【问题讨论】:

  • 您可以在问题中添加更多信息吗?我在 LinqPad 中运行代码并使用了非泛型重载。显示的代码(+编译所需的最少数量)不会重现您的问题。
  • 查看@Jon Skeet 对this question 的回答。
  • @hunch_hunch 我不确定这里的情况是否完全一样。
  • @anthony-pegram 抱歉,我不应该将 obj 设为对象的类型。我已更新问题以更准确地反映问题。
  • 好吧,如果类型不是对象,那么调用泛型重载是完全可以理解的,因为它完全匹配而非泛型重载不匹配。

标签: c# generics overloading


【解决方案1】:

编辑后这里是编译器选择的方法

DoSomething<SomeType,Type>(SomeType o, Type t)
DoSomething(object o,Type t);

正如规范所说的选择更好的函数成员:

给定一个带有一组参数表达式 { E1, E2, ..., EN } 和两个适用的函数成员 MP 和 MQ 带参数 类型 { P1, P2, ..., PN } 和 { Q1, Q2, ..., QN },MP 定义为 比 MQ 更好的函数成员 if
• 对于至少一个论点, EX转PX比EX转QX好。

泛型方法从SomeTypeSomeType 的转换比非泛型方法从SomeTypeobject 的转换效果更好。如果非泛型方法被定义为

DoSomething(SomeType o, Type t);

那么就不会有任何转换更好的参数,你会陷入大小写:

如果参数类型序列 {P1, P2, ..., PN} 和 {Q1, Q2, ..., QN} 等价

第一个规则会选择非泛型方法:

• 如果 MP 是非泛型方法且 MQ 是泛型方法,则 MP 是 比 MQ 好。


注意:在您编辑问题之前,有object 类型的第一个参数,而泛型方法是

DoSomething<object,Type>(object o, Type t)

同样,在这种情况下,参数序列是等价的,并且选择了非泛型方法。

【讨论】:

  • 虽然我觉得推断类型在解析树上应该更低,但我明白你在说通用版本的编译时间签名。
【解决方案2】:

在我的测试中,我能够使用强制转换来获得您想要的分辨率。 DoSomething((object)"1234", typeof(string));

【讨论】:

  • 该演员表在 OP 的代码中是无操作的;即使没有强制转换,它也会在显示的代码中使用非泛型重载。您必须将第一个参数的编译时类型更改为 object 以外的其他值,才能选择另一个重载。
  • 我的建议正确适用于已编辑的问题。它不适用于原始未编辑的问题。
  • 虽然这可行,但我想避免在这里装箱该变量。
  • @Nishmaster 那么这种类型是结构吗?如果是这样,您将需要使用泛型来避免装箱。如果不是,那么它没有被装箱。
  • 在您的示例代码中,您正在调用一个采用对象参数的方法,因此如果您使用值类型调用,则无法避免装箱。如果您使用引用类型调用,则不会发生装箱。
【解决方案3】:

呼叫 DoSomething&lt;object, SomeType&gt;(obj, type)();

【讨论】:

  • 我无法做到这一点,因为我在编译时不知道类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-08
  • 1970-01-01
相关资源
最近更新 更多