简体   繁体   English

扩展方法与父类方法行为

[英]Extension method vs parent class method behaviour

Look at the following code: 看下面的代码:

class A
{
    public string DoSomething(string str)
    {
        return "A.DoSomething: " + str;
    }
}

class B : A
{
}

static class BExtensions
{
    public static string DoSomething(this B b, string str)
    {
        return "BExtensions.DoSomething: " + str;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        var b = new B();
        Console.WriteLine(a.DoSomething("test"));
        Console.WriteLine(b.DoSomething("test"));

        Console.ReadKey();
    }
}

The output of the code is: 代码的输出是:

A.DoSomething: test A.DoSomething:测试

A.DoSomething: test A.DoSomething:测试

When it compiles it gives no warnings. 编译时,它不会发出警告。

My questions are: why there are no warnings when that code compiles and what exactly happens when the DoSomething method is called? 我的问题是:为什么在代码编译时没有警告,以及在调用DoSomething方法时究竟发生了什么?

What happens when the method is called is simple: just instance method call. 调用该方法时会发生什么很简单:只是实例方法调用。 Since C# is early-bound, all methods are resolved at compile-time. 由于C#是早期绑定的,因此所有方法都在编译时解析。 In addition, instance methods are preferred over extension ones, so this is why your extension method never gets invoked. 此外,实例方法比扩展方法更受欢迎,因此这就是永远不会调用扩展方法的原因。

See this : 看到这个

You can use extension methods to extend a class or interface, but not to override them. 您可以使用扩展方法来扩展类或接口,但不能覆盖它们。 An extension method with the same name and signature as an interface or class method will never be called. 永远不会调用与接口或类方法具有相同名称和签名的扩展方法。 At compile time, extension methods always have lower priority than instance methods defined in the type itself. 在编译时,扩展方法的优先级始终低于类型本身中定义的实例方法。

In other words, if a type has a method named Process(int i) , and you have an extension method with the same signature, the compiler will always bind to the instance method. 换句话说,如果类型具有名为Process(int i) ,并且您具有具有相同签名的扩展方法,则编译器将始终绑定到实例方法。 When the compiler encounters a method invocation, it first looks for a match in the type's instance methods. 当编译器遇到方法调用时,它首先在类型的实例方法中查找匹配项。 If no match is found, it will search for any extension methods that are defined for the type, and bind to the first extension method that it finds. 如果未找到匹配项,它将搜索为该类型定义的任何扩展方法,并绑定到它找到的第一个扩展方法。

Basically the compiler will always use an instance method if it's available, only resorting to extension methods when everything else fails. 基本上,编译器将始终使用实例方法(如果可用),仅在其他所有方法都失败时使用扩展方法。 From section 7.5.5.2 of the C# 3.0 spec: 从C#3.0规范的7.5.5.2节:

In a method invocation (§7.5.5.1) of one of the forms 在其中一个表单的方法调用(第7.5.5.1节)中

  • expr . expr。 identifier ( ) 标识符()
  • expr . expr。 identifier ( args ) 标识符(args)
  • expr . expr。 identifier < typeargs > ( ) 标识符<typeargs>()
  • expr . expr。 identifier < typeargs > ( args ) 标识符<typeargs>(args)

if the normal processing of the invocation finds no applicable methods, an attempt is made to process the construct as an extension method invocation. 如果调用的正常处理找不到适用的方法,则尝试将该构造作为扩展方法调用进行处理。

This is one of my niggles with the way extension methods are found... the extension method DoSomething will never be called as an extension method (although it's callable with normal static method syntax)... and yet the compiler doesn't even give a warning :( 这是我发现扩展方法的方式之一...扩展方法DoSomething永远不会被称为扩展方法(虽然它可以用普通的静态方法语法调用)...然而编译器甚至没有给出一个警告 :(

I may be not completely correct, but compiler does something like this: When it gets to 我可能不完全正确,但编译器做了这样的事情:当它到达时

b.DoSomething("test")

it tries to find method with same signature in this or base class, and when it finds the method in base class maps the call to it, simply ignoring extension method. 它试图在这个或基类中找到具有相同签名的方法,当它发现基类中的方法映射对它的调用时,只需忽略扩展方法。

But if you for example remove base class A from B declaration on the same line compiler will check that no method with such signature exists in this or base class and will replace it with call to static method BExtensions.DoSomething. 但是,如果您在同一行中从B声明中删除基类A,编译器将检查此类或基类中是否存在具有此类签名的方法,并将通过调用静态方法BExtensions.DoSomething来替换它。

You can check it out with .NET Reflector. 您可以使用.NET Reflector进行检查。

When B derives from A: 当B来自A时:

.locals init (
    [0] class Test.A a,
    [1] class Test.B b)
...
ldloc.1 // loading local variable b
ldstr "test"
callvirt instance string Test.A::DoSomething(string)
call void [mscorlib]System.Console::WriteLine(string)

When B derives from System.Object: 当B派生自System.Object时:

.locals init (
    [0] class Test.A a,
    [1] class Test.B b)
...
ldloc.1 // loading local variable b
ldstr "test"
call string Test.BExtensions::DoSomething(class Test.B, string)
call void [mscorlib]System.Console::WriteLine(string)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM