当多个方法重载匹配时,优先级是什么?

本文关键字:优先级 是什么 方法 重载 | 更新日期: 2023-09-27 18:15:52

我正在尝试理解c#中的OOP概念。

在下面的示例代码中:

  1. 为什么ins1更喜欢通用方法
  2. 为什么ins2, ins3更倾向于非通用方法

注意:当我注释掉"MyTestMethod"方法中的任何一个时,程序仍然继续成功运行。这段代码不是来自生产环境。这只是我的训练样本。所以,请不要介意命名约定和标准。

using System;
namespace ConsoleApplication1
{
    class Program
    {
        public static void MyTestMethod(J input)
        {
            Console.WriteLine($"Program.MyTestMethod: {input.Val}");
        }
        public static void MyTestMethod<T>(T input) where T : J
        {
            Console.WriteLine($"Program.MyTestMethod<T>: {input.Val}");
        }
        static void Main(string[] args)
        {
            J2 ins1 = new J2(1);
            MyTestMethod(ins1);
            J ins2 = new J(2);
            MyTestMethod(ins2);
            J ins3 = new J2(3);
            MyTestMethod(ins3);
            Console.ReadKey();
        }
    }
    internal class J
    {
        public int Val { get; set; }
        public J(int i)
        {
            Console.WriteLine($"concrete base {i}");
            Val = i;
        }
    }
    internal class J2 : J
    {
        public J2(int i) : base(i * -1)
        {
            Console.WriteLine($"concrete {i}");
        }
    }
}

当多个方法重载匹配时,优先级是什么?

c#规范的第7.5.3.2节在这里是相关的部分-"更好的函数成员"。

结果更简单地演示为:

using System;
class Test
{
    static void Foo<T>(T item)
    {
        Console.WriteLine("Generic");
    }
    
    static void Foo(object x)
    {
        Console.WriteLine("Non-generic");
    }
    
    static void Main()
    {
        Foo(new object()); // Calls Foo(object)
        Foo("test"); // Calls Foo<T>(T)
    }
}

在两个调用中,两个重载都是适用的函数成员。在选择调用哪个重载时,编译器首先检查从实参类型(或表达式)到形参类型的哪一种转换"更好"。

当参数类型为object时,T也被推断为object,因此对于两个候选对象来说,转换都是objectobject的恒等转换。这时,就涉及到7.5.3.2中的打破规则,第一个规则是:

如果M p 是非泛型方法,MQ是泛型方法,则M p 优于MQ

这就是为什么在这种情况下选择非泛型重载。

当参数类型为string时,T被推断为string,因此我们必须比较从stringstring的转换(对于泛型方法)和从stringobject的转换(对于非泛型方法)。这里出现了c#规范的第7.5.3.3节,它开始于:

给定将表达式E转换为T1类型的隐式转换C1和将表达式E转换为T2类型的隐式转换C2,如果至少满足下列条件之一,则C1是比C2更好的转换:

  • E具有S类型,存在从S到T1的恒等转换,但不存在从S到T2

在此上下文中,E是表达式"test", S是类型string, T1是类型string, T2是类型object,因此从字符串到字符串的转换被认为是更好的-并且选择泛型方法。

为什么ins2,3更喜欢非泛型方法

因为在这两种情况下,变量的类型(不是实例化的类型)是J,这与签名为MyTestMethod(J input)的方法完全匹配。因此,重载解析规则告诉它取那个。

为什么ins1更喜欢泛型方法

由于T可以在方法MyTestMethod(T input) where T : J中为J2,并且它可以将T强类型化为J2,因此,它比其他方法中的基类J更好地匹配。所以它用类型参数匹配方法