2017-03-18 30 views
1
public abstract class BaseClass 
{ 
    protected virtual int getValue() { return 1; } 
} 

Ich möchte getValue Methode aufrufen.So rufen Sie eine Methode in einer abstrakten Klasse auf

typeof(BaseClass) 
    .GetMethod("getValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) 
    .Invoke(null, new object[] { }); 

Dieser Code hat einen Fehler.

Nicht statische Methode erfordert ein Ziel

Allerdings kann ich nicht Instanz von Baseclass erstellt werden, weil abstrakte Klasse ist. So verwende ich Dummy-Klasse, die BaseClass erweitert.

class DummyClass : BaseClass { } 

typeof(BaseClass) 
    .GetMethod("getValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) 
    .Invoke(new DummyClass(), new object[] { }); 

Ich möchte Dummy-Klasse nicht jedes Mal erstellen, wenn möglich.

Gibt es einen besseren Weg?

+5

Wenn Sie eine Instanz erstellen müssen, markieren Sie die Klasse nicht als abstrakt. Sie können immer noch davon erben, solange es nicht als versiegelt markiert ist. So abstrakt funktioniert das, man kann nie eine direkte Instanz einer abstrakten Klasse schaffen ... überhaupt. Es gibt keinen Weg dahin. – Igor

+1

Aus welchem ​​Grund auch immer Sie dies tun, ist es wahrscheinlich die falsche Art, es zu tun. Sie können eine Methode für eine abstrakte Klasse nicht ausführen, es sei denn, statische Reflexion würde Sie nicht dazu bringen, dies zu tun. –

+0

Normalerweise erstelle ich keine Instanz von BaseClass. Aber ich brauche einen Rückgabewert von getValue beim Testen. Soll ich die abstrakte Klasse nur für das Testen in die Standardklasse ändern? – blz

Antwort

1

ich mit den anderen Kommentaren zustimmen, dass dies nicht die Produktion Code sein sollte, sondern aus Spaß es sicherlich getan werden kann:

public static T CreateAbstractInstance<T>() where T : class => 
    (T)Activator.CreateInstance(
     Thread.GetDomain() 
       .DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run) 
       .DefineDynamicModule("DynamicModule") 
       .DefineType("DynamicType", TypeAttributes.Public | TypeAttributes.Class, typeof(T)) 
       .CreateType()); 

private static TResult AbstractInvoke<TClass, TResult>(string methodName) where TClass : class 
{ 
    var method = typeof(TClass).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); 

    var elegate = method.CreateDelegate(typeof(Func<BaseClass, TResult>)); 

    return (TResult)elegate.DynamicInvoke(CreateAbstractInstance<TClass>()); 
} 

private static void Main() 
{ 
    var result = AbstractInvoke<BaseClass, int>("getValue"); 

    Console.WriteLine(result); 
} 

Es gibt wohl einige Methode, den Compiler zu betrügen und sie aufrufen, ohne auch nur die Schaffung eine Instanz, aber ich wurde zu viele Visual Studio Abstürze, um weiter zu untersuchen, andere Methoden. Ausdrücke scheinen nirgendwohin zu führen, wie am Ende des Tages, es musste immer noch gültigen Code erzeugen, also keine Möglichkeit, einen Ausdruck in ein ungültiges Lambda zu verwandeln.

Wenn Sie eine Singleton-Instanz von CreateAbstractInstance wiederverwenden, sollten Sie keine Overheads mehr haben, nachdem das erste Objekt erstellt wurde (neben Reflection-Verwendung, wenn Sie nicht öffentliche Methoden aufrufen).

Ich dachte, ich könnte dies auch auf die Antwort hinzufügen:

ich den Kommentar bemerken darüber in einem Unit-Test verwendet wird; wenn Sie Moq verwenden (und wahrscheinlich die meisten der Anständigen Frameworks), können Sie dies tun:

var bc = new Mock<BaseClass>().Object; 
// Invoke whatever you want on `bc` with reflection or not. 

Obwohl sich bewusst sein, dass, wenn die Methode virtual ist es automatisch außer Kraft setzen, so dass Sie nicht erhalten Standard die Logik der Basisklasse, aber es kann gelöst werden, den Schein einzurichten.

+0

Es funktioniert. Vielen Dank. – blz

+0

@blz Gern geschehen. By the way, wenn Sie es im Unit-Test verwenden, können Sie eine anständige Spott Framework (ich versuchte mit Moq -> 'neue Mock () .Object') erstellen Sie ein Mock der Basisklasse ohne ein konkrete Umsetzung und das könnte Ihrem Zweck gut dienen. –

0

Sie können versuchen, einen offenen Instanzdelegaten zu verwenden. Versuchen Sie folgendes:

var method = typeof(BaseClass).GetMethod("getValue"); 
var func = (Func<BaseClass, int>) Delegate.CreateDelegate(typeof(Func<BaseClass, int>), method); 
int result = func(null); 

Hinweis: Die obige Technik funktioniert durch die versteckten this Parameter in dem Elemente Anruf weggelassen wird. Dies funktioniert nur, wenn Ihre Methode nicht auf die Instanz zugreifen muss. Wenn Sie versuchen, auf die Instanz zuzugreifen, erhalten Sie eine Null-Referenzausnahme.

+0

es funktioniert nicht. "Objektreferenz ist nicht auf eine Objektinstanz festgelegt" – blz

+0

Etwas sagt mir, dass "getValue" mehr bringt als zurückgibt 1. Muss auf Membervariablen zugegriffen werden? –

+0

'getValue' greift nicht auf' this' zu. – blz

Verwandte Themen