[英]Clarify C# Extension Method Precedence
我正在尝试通过创建自己的具有相同名称的扩展方法来修改现有扩展方法的行为。 我知道这是可能的,只要方法签名不同即可。 我也知道调用哪种方法取决于签名的接近程度。
如果我有
public void DoStuff(this List<string> list) {
//...
}
和
public void DoStuff<T>(this List<T> list) {
//...
}
我知道DoStuff
将要求new List<string>().DoStuff()
而DoStuff<T>
将要求new List<int>().DoStuff()
我不明白的是为什么我在使用界面时看不到相同的行为。
public interface IBar { }
public class Foo : IBar
{
}
public static class FooHelper
{
public static void DoStuff(this IBar stuff)
{
Console.WriteLine("public static void DoStuff (this IBar stuff)");
}
public static void DoStuff<T>(this T stuff)
{
Console.WriteLine("public static void DoStuff<T>(this T stuff)");
}
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
foo.DoStuff();
}
}
在上面的示例中,我期望将public static void DoStuff (this IBar stuff)
输出到输出,但是我得到了public static void DoStuff<T>(this T stuff)
。 我假设接口类型比泛型类型更具体,因此我的DoStuff
调用是更好的选择。
为什么选择通用类型签名而不是接口类型签名?
克里斯的评论是正确的,以下是C#语言规范中的更多详细信息:
调用扩展方法foo.DoStuff()
等效于调用静态方法FooHelper.DoStuff(foo)
。
编译器必须确定要调用的确切方法(第7.6.5.1节方法调用)。 首先,它将创建一组候选方法。 这两个DoStuff方法都将在此集合中,因为
o如果F是非泛型的,则在以下情况下F是候选者:
•M没有类型参数列表,并且
•F适用于A(第7.5.3.1节)。
o如果F是泛型且M没有类型参数列表,则在以下情况下F是候选值:
•类型推断(第7.5.2节)成功,为调用推断类型实参列表,并且
•一旦将推断的类型参数替换为相应的方法类型参数,F的参数列表中的所有构造类型都将满足其约束(第4.4.4节),并且F的参数列表适用于A(第7.5节)。 3.1)。
通用FooHelper.DoStuff
方法将具有推断的类型参数(第7.5.2节):
类型推断是方法调用(第7.6.5.1节)的绑定时处理的一部分,发生在调用的重载解决步骤之前。
如果类型推断成功,则使用推断的类型实参确定用于后续重载解析的参数类型。
所以候选集是
public static void DoStuff(this IBar stuff)
public static void DoStuff<Foo>(this Foo stuff)
从这个集合中,编译器将选择最佳的调用方法。 这些规则在第7.5.3.2节中进行了描述。 在这种情况下,将调用通用方法,因为身份转换(从Foo到Foo)比从Foo到IBar的转换更好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.