2013-05-07 6 views
6

Hier ist eine einfache Anwendung, die die Methode Signatur eines MethodCallExpression druckt:MethodCallExpression.Method gibt immer die Wurzel Basisklasse Method

using System; 
using System.Linq; 
using System.Linq.Expressions; 

class A 
{ 
    public virtual void Foo() { } 
} 

class B : A 
{ 
    public override void Foo() { } 
} 

class C : B 
{ 
    public override void Foo() { } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     PrintMethod<A>(a => a.Foo()); 
     PrintMethod<B>(b => b.Foo()); 
     PrintMethod<C>(c => c.Foo()); 

     Console.Read(); 
    } 

    static void PrintMethod<T>(Expression<Action<T>> expression) 
    { 
     var body = (MethodCallExpression)expression.Body; 

     var method1 = body.Method; 
     var method2 = typeof(T).GetMethod(body.Method.Name, body.Method.GetParameters().Select(p => p.ParameterType).ToArray()); 

     Console.WriteLine("body.Method   -> " + method1.DeclaringType.ToString() + " - " + method1.ToString()); 
     Console.WriteLine("typeof(T).GetMethod -> " + method2.DeclaringType.ToString() + " - " + method2.ToString()); 
    } 
} 

ich das Programm auszudrucken erwarten:

body.Method   -> A - Void Foo() 
typeof(T).GetMethod -> A - Void Foo() 
body.Method   -> B - Void Foo() * 
typeof(T).GetMethod -> B - Void Foo() 
body.Method   -> C - Void Foo() * 
typeof(T).GetMethod -> C - Void Foo() 

Aber es druckt stattdessen aus:

body.Method   -> A - Void Foo() 
typeof(T).GetMethod -> A - Void Foo() 
body.Method   -> A - Void Foo() * 
typeof(T).GetMethod -> B - Void Foo() 
body.Method   -> A - Void Foo() * 
typeof(T).GetMethod -> C - Void Foo() 

Wenn Sie diebekommenEigenschaft für die vererbte MethodCallExpression, gibt es immer A s MethodInfo (die Stammklasse) zurück.

Allerdings, in Visual Studio und ich "Go to Definition" von jedem der Foo() Aufrufe, bin ich zu jeder der überschriebenen Methoden wie erwartet übernommen.

Warum verhält sich das MethodCallExpression.Method so? Gibt es irgendetwas in der Spezifikation? Warum gibt es eine Diskrepanz zwischen VS und der Method Eigenschaft? Ich habe mit .NET 4.0 und 4.5 getestet.

+0

Gute Frage. Dieses Verhalten scheint intuitiv korrekt zu sein, wenn man an den generierten Code denkt - in allen 3 Fällen ist es ein Callvirt zu "A.Foo". –

+0

Dieses Verhalten wurde im C# -Sprache-Design-Repository auf GitHub erwähnt: siehe https://github.com/dotnet/roslyn/issues/24347#issuecomment-359070141. – stakx

Antwort

4

Angenommen, Sie eine Bibliothek:

public class A 
{ 
    public virtual void Foo() { } 
} 

public class B : A 
{ 
    public override void Foo() { } 
} 

public class C : B 
{ 
    public override void Foo() { } 
} 

Und Sie haben einen Verbraucher, die

new C().Foo(); 

Jetzt hat die Bibliothek zu aktualisieren, so dass C nicht mehr überschreibt Foo:

public class C : B 
{ 
} 

Muss der Verbraucher neu kompiliert werden?

Wenn der Verbraucher C.Foo virtuell aufruft, dann ja, und der Verbraucher müsste ((A)new C()).Foo() speziell schreiben, um dieses Problem zu vermeiden. Wenn der Verbraucher A.Foo virtuell anruft, dann nein. Da dies der einzige Unterschied ist, da die exakt gleiche Funktion zur Laufzeit aufgerufen wird, macht es für den Verbraucher keinen Sinn zu spezifizieren, dass er C.Foo aufruft.

Ausdrucksbäume zeichnen dieselben Methodeninformationen auf, die ein normaler Funktionsaufruf aufzeichnen würde. Die C# Spezifikation hat sehr wenig darüber zu sagen, es läßt sich die Implementierung definiert (noch Microsofts Implementierung scheint nicht zu definieren (document) it):

Umwandlung einer anonymen Funktion in einen Ausdruck Baumtyp erzeugt ein Ausdrucksbaum (§4.6). Genauer gesagt führt die Auswertung der anonymen Funktionskonvertierung zur Konstruktion einer Objektstruktur, die die Struktur der anonymen Funktion selbst darstellt. Die genaue Struktur des Ausdrucksbaums sowie der genaue Prozess für die Erstellung des Ausdrucksbaums sind in der Implementierung definiert.

+0

Ja das macht jetzt Sinn, dass du es erklärt hast. Es ist jedoch nicht sehr offensichtlich. Ich hätte erwartet, dass VS und der Ausdrucksbaum sich ähnlich verhalten würden. – TheCloudlessSky

+0

@TheCloudlessSky Einverstanden, dass es nicht offensichtlich ist, aber ich bin froh, dass die VS-Entwickler eine Ausnahme gemacht haben, um "Gehe zur Definition" intuitiver zu machen, auch wenn es sich anders verhält als C#. :) – hvd