当多个方法重载匹配时,优先级是什么?
本文关键字:优先级 是什么 方法 重载 | 更新日期: 2023-09-27 18:15:52
我正在尝试理解c#中的OOP概念。
在下面的示例代码中:
- 为什么
ins1
更喜欢通用方法 - 为什么
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
,因此对于两个候选对象来说,转换都是object
到object
的恒等转换。这时,就涉及到7.5.3.2中的打破规则,第一个规则是:
如果M p 是非泛型方法,MQ是泛型方法,则M p 优于MQ。
这就是为什么在这种情况下选择非泛型重载。
当参数类型为string
时,T
被推断为string
,因此我们必须比较从string
到string
的转换(对于泛型方法)和从string
到object
的转换(对于非泛型方法)。这里出现了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
更好地匹配。所以它用类型参数匹配方法