【问题标题】:Generic class can product identical signature overloads? [duplicate]泛型类可以产生相同的签名重载吗? [复制]
【发布时间】:2012-10-23 14:26:06
【问题描述】:

可能重复:
Generic methods and method overloading

好吧,我不小心撞到了这个……鉴于这种情况:

class Program {
    static void Main( string[ ] args ) {

        var obj = new gen<int>( );
        Console.Write( obj[ 1 ] );
        Console.ReadKey( );

    }
}

class gen<T> {

    public int this[ T i ] { get { return 2; } }

    public int this[ int i ] { get { return 1; } }

}

它总是会打印 1。我希望编译器会抱怨,或者运行时会崩溃并烧毁 CPU,但是不,很高兴打印 '1'

当然,如果我对泛型参数使用任何其他类型,我可以选择返回。对于傻笑,我尝试使用 UInt 作为泛型类型参数,我可以区分调用,所以问题是:

  1. 为什么 C# 不会崩溃? Anders Hejlsberg 不应该感到原力受到干扰吗?

  2. 如何限制某些类型的泛型参数?因为在这个 T 可以是除整数以外的任何东西(但 long 是可以的)

【问题讨论】:

  • 在 SO(例如 here )上询问并回答了像 where T : anything-but-int 这样的约束(简短回答:你不能)
  • 我进行了快速搜索,但都没有弹出...但感谢您的指点

标签: c#


【解决方案1】:

我相信这是在 C# 4 规范(更好的函数成员)的第 7.5.3.2 节中指定的。

  • 否则,如果 MP 具有比 MQ 更具体的参数类型,则 MP 优于 MQ [...]
    • 类型参数不如非类型参数具体
    • [...]

所以在这里,带有T 参数的成员没有带有int 参数的成员那么具体。

尝试设计出摆脱这种情况的方法,而不是像这样重载。当然,这对索引器来说很难,但您总是可以提供方法来代替(或者也可以作为后备)。

编辑:请注意,如果您有重载方法,其中 both 都是类型参数,编译器抱怨:

public class Foo<T1, T2>
{
    public void Bar(T1 t1) {}
    public void Bar(T2 t2) {}
}

...

Foo<int, int> foo = new Foo<int, int>();
foo.Bar(10); // Error

这两种方法都不是更具体的。

如何限制某些类型的泛型参数?因为在这个 T 可以是除整数以外的任何东西(但可以长)

这确实是一个完全独立的问题,但基本上你不能。您可以通过各种方式约束类型参数,但不能通过显式包含和排除类型。请参阅the MSDN page on constraints 了解更多信息。

【讨论】:

  • 该死,我刚开始读这个问题,我的第一个想法是,我敢打赌飞碟人会知道这个问题
  • @MikeyMouse:我不想让你失望,但我确实必须把规范拿出来......
  • 所以底线...这是规范的一部分,它将使用更显式的版本而不是隐式版本...没关系,对我来说听起来有点接近“不是一个错误,是一个功能”,但我会接受它(我没有任何其他选择)
  • @JorgeLeo:我确信 C# 团队已经进行了彻底的考虑 - 我强烈怀疑您提出的任何替代设计都会有其他不愉快的边缘情况。跨度>
  • 第二个问题的答案是不需要在语言中添加任何内容,因此开发人员可以在调用中说出要支持的内容,因为该问题已在规范中解决。跨度>
【解决方案2】:

作为Eric Lippert says:

C# 规范规定,当您可以在调用 real-doit(string) 和 real-doit(string) 之间进行选择时——也就是说, 当在具有相同签名的两种方法之间进行选择时, 但是一个人通过通用替换获得了这个签名——然后我们选择 “自然”签名优于“替代”签名。

C# 规范 7.5.3.2(更好的函数成员)中也描述了这个过程:

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

  • 如果 MP 是非泛型方法而 MQ 是泛型方法,那么 MP 比 MQ 更好(正如 John 指出的那样,当你有泛型方法而不是泛型类型时,这是正确的)
  • ...
  • 否则,如果 MP 比 MQ 有更具体的参数类型,那么 MP 比 MQ 更好。令 {R1, R2, ..., RN} 和 {S1, S2, ..., SN} 表示 MP 和 MQ 的未实例化和未扩展的参数类型。 MP 的参数类型比 MQ 的更具体,如果对于每个参数,RX 不比 SX 更具体,并且,对于至少一个参数,RX 比 SX 更具体:

    • 类型参数不如非类型参数具体(这是您的情况 - 因此方法不是泛型的,推断的参数类型等于非泛型参数类型)

【讨论】:

  • 您突出显示的部分不适用于此处 - 这些不是通用方法。它们是泛型类型中的非泛型成员。请参阅我对此处适用的规范部分的回答。
  • @JonSkeet 同意,为你 +1,为我更新
【解决方案3】:

如果调用同时适合两个 =),C# 编译器总是会选择更具体而不是更通用的方法。这就是为什么它没有吓坏,他只是遵守他的规则。

【讨论】:

    【解决方案4】:

    C# 编译器不会崩溃,因为这两个方法都是有效的,并且都可以调用。

    这是一个返回“2”的示例:

    Gen<Form> gen = new Gen<Form>();
    textBox1.Text = gen[this].ToString();
    

    其中“this”是一种形式。当然,使用索引访问器作为对象而不是数字......好吧,无论如何,它可以工作。

    但就像其他人所说的那样,编译器会更喜欢显式而不是隐式。

    【讨论】:

    • 这不取决于 JIT - 这取决于 C# 编译器。此外,仅仅因为在 some 情况下您可以调用它并不一定意味着它在 this 情况下有效。在您有两个类型参数和接受每个类型参数的重载成员的情况下,C# 编译器抱怨 - 但是您的“如果您做不同的事情,您可以调用它”参数仍然可用...
    • 好的,编译器。我的错。 “某些情况”与“这种情况”毫无意义,因为没有约束,编译器无法知道 T 是什么。在我看来,T 可能是一个土豆,两种方法都非常有效,都可以独立调用。
    • 不,这根本不是毫无意义的。 OP 并没有问为什么编译器没有抱怨在他的特定情况下。您的代码不能证明任何事情。我不相信 OP 是在问为什么要编译 gen&lt;T&gt; - 他是在问为什么他在 gen&lt;T&gt;调用索引器的方式已编译。
    • 好吧...反过来,为什么它不能编译?他将 1 传递给一个方法,其中一个方法重载对此参数有明确的定义。编译和使用哪种方法是两件不同的事情。
    • 请参阅我的答案以获取无法编译的示例 - 其中 both 是泛型类型参数。 可以定义语言,以便在类型参数列表中较早声明的类型参数比稍后声明的类型参数更具体......但事实并非如此。基本上可以在这里以多种方式定义语言。
    猜你喜欢
    • 2011-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多