Wie verwende ich Reflektion, um eine generische Methode aufzurufen?

1070

Wie kann eine generische Methode am besten aufgerufen werden, wenn der Typparameter zur Kompilierungszeit nicht bekannt ist, sondern zur Laufzeit dynamisch abgerufen wird?

Betrachten Sie den folgenden Beispielcode Example(): Was ist innerhalb der Methode die prägnanteste Methode zum Aufrufen GenericMethod<T>()mit dem Typein der myTypeVariablen gespeicherten Code ?

public class Sample
{
    public void Example(string typeName)
    {
        Type myType = FindType(typeName);

        // What goes here to call GenericMethod<T>()?
        GenericMethod<myType>(); // This doesn't work

        // What changes to call StaticMethod<T>()?
        Sample.StaticMethod<myType>(); // This also doesn't work
    }

    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}
Bevan
quelle
7
Ich habe Jons Lösung ausprobiert und konnte sie erst zum Laufen bringen, als ich die generische Methode in meiner Klasse veröffentlicht hatte. Ich weiß, dass ein anderer Jon geantwortet hat, dass Sie die Bindungsflags angeben müssen, aber das hat nicht geholfen.
Naskew
12
Sie müssen BindingFlags.Instanceauch nicht nur BindingFlags.NonPublicdie private / interne Methode erhalten.
Lars Kemmann
2
Moderne Version dieser Frage: stackoverflow.com/q/2433436/103167
Ben Voigt
@Peter Mortensen - fyi Ich habe Leerzeichen vor dem '?' die englischen Teile von den nicht englischen (C #) Teilen zu trennen; IMHO das Entfernen des Leerzeichens lässt es wie das aussehen? ist Teil des Codes. Wenn es keinen Code gäbe, würde ich dem Entfernen der Leerzeichen sicherlich zustimmen, aber in diesem Fall ...
Bevan

Antworten:

1138

Sie müssen Reflection verwenden, um die Methode zu starten, und sie dann "konstruieren", indem Sie Typargumente mit MakeGenericMethod angeben :

MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.GenericMethod));
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Übergeben Sie für eine statische Methode nullals erstes Argument an Invoke. Das hat nichts mit generischen Methoden zu tun - es ist nur normale Reflexion.

Wie bereits erwähnt, ist vieles davon ab C # 4 einfacher dynamic- wenn Sie die Typinferenz verwenden können, natürlich. Es hilft nicht in Fällen, in denen keine Typinferenz verfügbar ist, wie im genauen Beispiel in der Frage.

Jon Skeet
quelle
92
+1; Beachten Sie, dass GetMethod()standardmäßig nur öffentliche Instanzmethoden berücksichtigt werden, sodass Sie möglicherweise BindingFlags.Staticund / oder benötigen BindingFlags.NonPublic.
20
Die richtige Kombination von Flags ist BindingFlags.NonPublic | BindingFlags.Instance(und optional BindingFlags.Static).
Lars Kemmann
4
Eine Frage, die als betrügerisch eingestuft wird, fragt sich, wie dies mit statischen Methoden geschehen soll - und technisch gesehen auch die Frage hier. Der erste Parameter von generic.Invoke () sollte beim Aufrufen statischer Methoden null sein. Der erste Parameter ist nur beim Aufrufen von Instanzmethoden erforderlich.
Chris Moschini
2
@ ChrisMoschini: Das wurde der Antwort hinzugefügt.
Jon Skeet
2
@gzou: Ich habe etwas auf die Antwort hinzugefügt - aber zur Kenntnis , dass die generischen Methoden für den Aufruf in der Frage , dynamicnicht hilft , weil Typinferenz nicht verfügbar ist. (Es gibt keine Argumente, mit denen der Compiler das Typargument bestimmen kann.)
Jon Skeet
170

Nur eine Ergänzung zur ursprünglichen Antwort. Während dies funktionieren wird:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Es ist auch ein wenig gefährlich, dass Sie die Überprüfung der Kompilierungszeit für verlieren GenericMethod. Wenn Sie später ein Refactoring durchführen und umbenennen GenericMethod, wird dieser Code nicht bemerkt und schlägt zur Laufzeit fehl. Wenn die Assembly nachbearbeitet wird (z. B. nicht verwendete Methoden / Klassen verschleiern oder entfernen), kann dieser Code ebenfalls beschädigt werden.

Wenn Sie also die Methode kennen, mit der Sie zur Kompilierungszeit verknüpfen, und diese nicht millionenfach aufgerufen wird, sodass der Overhead keine Rolle spielt, würde ich diesen Code folgendermaßen ändern:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Obwohl dies nicht sehr hübsch ist, haben Sie hier einen Verweis auf die Kompilierungszeit. GenericMethodWenn Sie den GenericMethodCode umgestalten, löschen oder etwas damit tun , funktioniert er weiter oder bricht zumindest zur Kompilierungszeit ab (wenn Sie ihn beispielsweise entfernen GenericMethod).

Eine andere Möglichkeit, dasselbe zu tun, besteht darin, eine neue Wrapper-Klasse zu erstellen und diese zu erstellen Activator. Ich weiß nicht, ob es einen besseren Weg gibt.

Adrian Gallero
quelle
5
In Fällen, in denen Reflection zum Aufrufen einer Methode verwendet wird, wird der Methodenname normalerweise selbst von einer anderen Methode erkannt. Es ist nicht üblich, den Methodennamen im Voraus zu kennen.
Bevan
13
Nun, ich stimme den üblichen Verwendungszwecken der Reflexion zu. Die ursprüngliche Frage war jedoch, wie "GenericMethod <myType> ()" aufgerufen werden soll. Wenn diese Syntax zulässig wäre, würden wir GetMethod () überhaupt nicht benötigen. Aber für die Frage "Wie schreibe ich" GenericMethod <myType> "? Ich denke, die Antwort sollte einen Weg enthalten, um zu vermeiden, dass die Verbindung zur Kompilierungszeit mit GenericMethod verloren geht. Ob diese Frage nun häufig ist oder nicht, weiß ich nicht, aber Ich weiß, dass ich gestern genau dieses Problem hatte, und deshalb bin ich in dieser Frage gelandet.
Adrian Gallero
20
Sie tun können , GenMethod.Method.GetGenericMethodDefinition()statt this.GetType().GetMethod(GenMethod.Method.Name). Es ist etwas sauberer und wahrscheinlich sicherer.
Daniel Cassidy
Was bedeutet "myType" in Ihrem Beispiel?
Entwickler
37
Jetzt können Sie verwendennameof(GenericMethod)
dmigo
140

Das Aufrufen einer generischen Methode mit einem nur zur Laufzeit bekannten Typparameter kann durch die Verwendung eines dynamicTyps anstelle der Reflection-API erheblich vereinfacht werden .

Um diese Technik verwenden zu können, muss der Typ aus dem tatsächlichen Objekt bekannt sein (nicht nur aus einer Instanz der TypeKlasse). Andernfalls müssen Sie ein Objekt dieses Typs erstellen oder die Standard-Reflection-API- Lösung verwenden . Sie können ein Objekt mithilfe der Activator.CreateInstance- Methode erstellen .

Wenn Sie eine generische Methode aufrufen möchten, deren Typ bei "normaler" Verwendung abgeleitet worden wäre, wird einfach das Objekt eines unbekannten Typs in umgewandelt dynamic. Hier ist ein Beispiel:

class Alpha { }
class Beta { }
class Service
{
    public void Process<T>(T item)
    {
        Console.WriteLine("item.GetType(): " + item.GetType()
                          + "\ttypeof(T): " + typeof(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new Alpha();
        var b = new Beta();

        var service = new Service();
        service.Process(a); // Same as "service.Process<Alpha>(a)"
        service.Process(b); // Same as "service.Process<Beta>(b)"

        var objects = new object[] { a, b };
        foreach (var o in objects)
        {
            service.Process(o); // Same as "service.Process<object>(o)"
        }
        foreach (var o in objects)
        {
            dynamic dynObj = o;
            service.Process(dynObj); // Or write "service.Process((dynamic)o)"
        }
    }
}

Und hier ist die Ausgabe dieses Programms:

item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta
item.GetType(): Alpha    typeof(T): System.Object
item.GetType(): Beta     typeof(T): System.Object
item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta

Processist eine generische Instanzmethode, die den realen Typ des übergebenen Arguments (mithilfe der GetType()Methode) und den Typ des generischen Parameters (mithilfe des typeofOperators) schreibt .

Durch Umwandeln des Objektarguments in dynamicTyp haben wir die Bereitstellung des Typparameters bis zur Laufzeit verschoben. Wenn die ProcessMethode mit dem dynamicArgument aufgerufen wird, kümmert sich der Compiler nicht um den Typ dieses Arguments. Der Compiler generiert Code, der zur Laufzeit die tatsächlichen Typen der übergebenen Argumente überprüft (mithilfe von Reflection) und die beste aufzurufende Methode auswählt. Hier gibt es nur diese eine generische Methode, daher wird sie mit einem geeigneten Typparameter aufgerufen.

In diesem Beispiel ist die Ausgabe dieselbe, als ob Sie geschrieben hätten:

foreach (var o in objects)
{
    MethodInfo method = typeof(Service).GetMethod("Process");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(service, new object[] { o });
}

Die Version mit einem dynamischen Typ ist definitiv kürzer und einfacher zu schreiben. Sie sollten sich auch keine Gedanken über die Leistung machen, wenn Sie diese Funktion mehrmals aufrufen. Der nächste Aufruf mit Argumenten des gleichen Typs sollte dank des Caching- Mechanismus im DLR schneller sein . Natürlich können Sie Code schreiben, der aufgerufene Delegaten zwischenspeichert, aber wenn Sie den dynamicTyp verwenden, erhalten Sie dieses Verhalten kostenlos.

Wenn die generische Methode, die Sie aufrufen möchten, kein Argument eines parametrisierten Typs enthält (sodass der Typparameter nicht abgeleitet werden kann), können Sie den Aufruf der generischen Methode in eine Hilfsmethode wie im folgenden Beispiel einschließen:

class Program
{
    static void Main(string[] args)
    {
        object obj = new Alpha();

        Helper((dynamic)obj);
    }

    public static void Helper<T>(T obj)
    {
        GenericMethod<T>();
    }

    public static void GenericMethod<T>()
    {
        Console.WriteLine("GenericMethod<" + typeof(T) + ">");
    }
}

Erhöhte Typensicherheit

Das dynamicBesondere an der Verwendung von Objekten als Ersatz für die Verwendung der Reflection-API ist, dass Sie nur die Überprüfung der Kompilierungszeit dieses bestimmten Typs verlieren, die Sie erst zur Laufzeit kennen. Andere Argumente und der Name der Methode werden vom Compiler wie gewohnt statisch analysiert. Wenn Sie weitere Argumente entfernen oder hinzufügen, deren Typen ändern oder den Methodennamen umbenennen, wird ein Fehler beim Kompilieren angezeigt. Dies ist nicht der Fall, wenn Sie den Methodennamen als Zeichenfolge Type.GetMethodund Argumente als Objektarray angeben MethodInfo.Invoke.

Im Folgenden finden Sie ein einfaches Beispiel, das zeigt, wie einige Fehler zur Kompilierungszeit (kommentierter Code) und andere zur Laufzeit abgefangen werden können. Es zeigt auch, wie das DLR versucht, die aufzurufende Methode aufzulösen.

interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }

class Program
{
    static void Main(string[] args)
    {
        var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
        for (int i = 0; i < objects.Length; i++)
        {
            ProcessItem((dynamic)objects[i], "test" + i, i);

            //ProcesItm((dynamic)objects[i], "test" + i, i);
            //compiler error: The name 'ProcesItm' does not
            //exist in the current context

            //ProcessItem((dynamic)objects[i], "test" + i);
            //error: No overload for method 'ProcessItem' takes 2 arguments
        }
    }

    static string ProcessItem<T>(T item, string text, int number)
        where T : IItem
    {
        Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
                          typeof(T), text, number);
        return "OK";
    }
    static void ProcessItem(BarItem item, string text, int number)
    {
        Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
    }
}

Hier führen wir erneut eine Methode aus, indem wir das Argument in den dynamicTyp umwandeln. Nur die Überprüfung des Typs des ersten Arguments wird auf die Laufzeit verschoben. Sie erhalten einen Compilerfehler, wenn der Name der von Ihnen aufgerufenen Methode nicht vorhanden ist oder wenn andere Argumente ungültig sind (falsche Anzahl von Argumenten oder falsche Typen).

Wenn Sie das dynamicArgument an eine Methode übergeben, ist dieser Aufruf kürzlich gebunden . Die Auflösung der Methodenüberladung erfolgt zur Laufzeit und versucht, die beste Überladung auszuwählen. Wenn Sie also die ProcessItemMethode mit einem Objekt vom BarItemTyp aufrufen, rufen Sie die nicht generische Methode auf, da sie besser zu diesem Typ passt. Wenn Sie ein Argument des AlphaTyps übergeben , wird jedoch ein Laufzeitfehler angezeigt, da es keine Methode gibt, die dieses Objekt verarbeiten kann (eine generische Methode hat die Einschränkung where T : IItemund die AlphaKlasse implementiert diese Schnittstelle nicht). Aber das ist der springende Punkt. Der Compiler hat keine Informationen darüber, dass dieser Aufruf gültig ist. Sie als Programmierer wissen dies und sollten sicherstellen, dass dieser Code fehlerfrei ausgeführt wird.

Rückgabetyp gotcha

Wenn Sie eine nicht-void - Methode mit einem Parameter der dynamischen Art Aufruf, wird seine Rückkehr Art wahrscheinlich sein dynamiczu . Wenn Sie also das vorherige Beispiel in diesen Code ändern würden:

var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

dann wäre der Typ des Ergebnisobjekts dynamic. Dies liegt daran, dass der Compiler nicht immer weiß, welche Methode aufgerufen wird. Wenn Sie den Rückgabetyp des Funktionsaufrufs kennen, sollten Sie ihn implizit in den erforderlichen Typ konvertieren , damit der Rest des Codes statisch typisiert wird:

string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

Sie erhalten einen Laufzeitfehler, wenn der Typ nicht übereinstimmt.

Wenn Sie versuchen, den Ergebniswert im vorherigen Beispiel abzurufen, wird in der zweiten Schleifeniteration ein Laufzeitfehler angezeigt. Dies liegt daran, dass Sie versucht haben, den Rückgabewert einer void-Funktion zu speichern.

Mariusz Pawelski
quelle
Mariusz, verwirrt von "Sie erhalten jedoch einen Laufzeitfehler, wenn Sie ein Argument vom Typ Alpha übergeben, da es keine Methode gibt, die dieses Objekt verarbeiten kann." Wenn ich var a = new Alpha () ProcessItem (a, "test" + i) aufrufe , i) Warum würde die generische ProcessItem-Methode dies nicht effektiv handhaben und "General Process Item" ausgeben?
Alex Edelstein
@AlexEdelstein Ich habe meine Antwort bearbeitet, um ein bisschen zu klären. Dies liegt daran, dass die generische ProcessItemMethode generische Einschränkungen aufweist und nur Objekte akzeptiert, die die IItemSchnittstelle implementieren . Wenn Sie anrufen ProcessItem(new Aplha(), "test" , 1);oder ProcessItem((object)(new Aplha()), "test" , 1);eine Compiler-Fehlermeldung erhalten, aber beim Casting dynamicdie Überprüfung auf Laufzeit verschieben.
Mariusz Pawelski
Tolle Antwort und Erklärung, funktioniert perfekt für mich. Viel besser als die akzeptierte Antwort, kürzer zu schreiben, performanter und sicherer.
Ygoe
17

Mit C # 4.0 ist keine Reflexion erforderlich, da das DLR sie mithilfe von Laufzeittypen aufrufen kann. Da die Verwendung der DLR-Bibliothek dynamisch schwierig ist (anstelle des C # -Compilers, der Code für Sie generiert), bietet Ihnen das Open-Source-Framework Dynamitey (.net Standard 1.5) einen einfachen zwischengespeicherten Laufzeitzugriff auf dieselben Aufrufe, die der Compiler generieren würde für dich.

var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));
jbtule
quelle
13

Hinzu kommt die Antwort von Adrian Gallero :

Das Aufrufen einer generischen Methode aus Typinformationen umfasst drei Schritte.

TLDR: Das Aufrufen einer bekannten generischen Methode mit einem Typobjekt kann folgendermaßen erfolgen:

((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition()
    .MakeGenericMethod(typeof(string))
    .Invoke(this, null);

Wo GenericMethod<object>ist der aufzurufende Methodenname und jeder Typ, der die allgemeinen Einschränkungen erfüllt?

(Aktion) entspricht der Signatur der aufzurufenden Methode, dh ( Func<string,string,int>oder Action<bool>)

In Schritt 1 wird die MethodInfo für die generische Methodendefinition abgerufen

Methode 1: Verwenden Sie GetMethod () oder GetMethods () mit geeigneten Typen oder Bindungsflags.

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");

Methode 2: Erstellen Sie einen Delegaten, rufen Sie das MethodInfo-Objekt ab und rufen Sie GetGenericMethodDefinition auf

Aus der Klasse heraus, die die Methoden enthält:

MethodInfo method = ((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

Von außerhalb der Klasse, die die Methoden enthält:

MethodInfo method = ((Action)(new Sample())
    .GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)Sample.StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

In C # bezieht sich der Name einer Methode, dh "ToString" oder "GenericMethod", tatsächlich auf eine Gruppe von Methoden, die eine oder mehrere Methoden enthalten können. Bis Sie die Typen der Methodenparameter angeben, ist nicht bekannt, auf welche Methode Sie sich beziehen.

((Action)GenericMethod<object>)bezieht sich auf den Delegaten für eine bestimmte Methode. ((Func<string, int>)GenericMethod<object>) verweist auf eine andere Überladung von GenericMethod

Methode 3: Erstellen Sie einen Lambda-Ausdruck, der einen Methodenaufrufausdruck enthält, rufen Sie das MethodInfo-Objekt und dann GetGenericMethodDefinition ab

MethodInfo method = ((MethodCallExpression)((Expression<Action<Sample>>)(
    (Sample v) => v.GenericMethod<object>()
    )).Body).Method.GetGenericMethodDefinition();

Das bricht zusammen zu

Erstellen Sie einen Lambda-Ausdruck, bei dem der Körper die gewünschte Methode aufruft.

Expression<Action<Sample>> expr = (Sample v) => v.GenericMethod<object>();

Extrahieren Sie den Body und wandeln Sie ihn in MethodCallExpression um

MethodCallExpression methodCallExpr = (MethodCallExpression)expr.Body;

Rufen Sie die generische Methodendefinition aus der Methode ab

MethodInfo methodA = methodCallExpr.Method.GetGenericMethodDefinition();

Schritt 2 ruft MakeGenericMethod auf, um eine generische Methode mit den entsprechenden Typen zu erstellen.

MethodInfo generic = method.MakeGenericMethod(myType);

Schritt 3 ruft die Methode mit den entsprechenden Argumenten auf.

generic.Invoke(this, null);
Grax32
quelle
8

Niemand hat die " klassische Reflection " -Lösung bereitgestellt. Hier ist ein vollständiges Codebeispiel:

using System;
using System.Collections;
using System.Collections.Generic;

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

Die obige DynamicDictionaryFactoryKlasse hat eine Methode

CreateDynamicGenericInstance(Type keyType, Type valueType)

und es erstellt und gibt eine IDictionary-Instanz zurück, deren Schlüssel und Werte genau den im Aufruf keyTypeund angegebenen Werten entsprechen valueType.

Hier ist ein vollständiges Beispiel, wie diese Methode aufgerufen wird, um Folgendes zu instanziieren und zu verwenden Dictionary<String, int>:

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

Wenn die obige Konsolenanwendung ausgeführt wird, erhalten wir das richtige, erwartete Ergebnis:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3
Dimitre Novatchev
quelle
2

Dies sind meine 2 Cent basierend auf Grax 'Antwort , aber mit zwei Parametern, die für eine generische Methode erforderlich sind.

Angenommen, Ihre Methode ist in einer Helpers-Klasse wie folgt definiert:

public class Helpers
{
    public static U ConvertCsvDataToCollection<U, T>(string csvData)
    where U : ObservableCollection<T>
    {
      //transform code here
    }
}

In meinem Fall ist der Typ U immer ein beobachtbares Sammlungsobjekt vom Typ T.

Da ich meine Typen vordefiniert habe, erstelle ich zuerst die "Dummy" -Objekte, die die beobachtbare Sammlung (U) und das darin gespeicherte Objekt (T) darstellen und die unten verwendet werden, um ihren Typ beim Aufrufen von Make abzurufen

object myCollection = Activator.CreateInstance(collectionType);
object myoObject = Activator.CreateInstance(objectType);

Rufen Sie dann die GetMethod auf, um Ihre generische Funktion zu finden:

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

Bisher ist der obige Aufruf ziemlich identisch mit dem oben erläuterten, jedoch mit einem kleinen Unterschied, wenn Sie mehrere Parameter an ihn übergeben müssen.

Sie müssen ein Array vom Typ [] an die Funktion MakeGenericMethod übergeben, die die oben erstellten "Dummy" -Objekttypen enthält:

MethodInfo generic = method.MakeGenericMethod(
new Type[] {
   myCollection.GetType(),
   myObject.GetType()
});

Sobald dies erledigt ist, müssen Sie die Invoke-Methode wie oben erwähnt aufrufen.

generic.Invoke(null, new object[] { csvData });

Und du bist fertig. Wirkt ein Zauber!

AKTUALISIEREN:

Wie @Bevan hervorgehoben hat, muss ich beim Aufrufen der MakeGenericMethod-Funktion kein Array erstellen, da sie Parameter enthält, und ich muss kein Objekt erstellen, um die Typen abzurufen, da ich die Typen einfach direkt an diese Funktion übergeben kann. In meinem Fall habe ich meinen Code einfach geändert, da ich die Typen in einer anderen Klasse vordefiniert habe:

object myCollection = null;

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

MethodInfo generic = method.MakeGenericMethod(
   myClassInfo.CollectionType,
   myClassInfo.ObjectType
);

myCollection = generic.Invoke(null, new object[] { csvData });

myClassInfo enthält 2 Eigenschaften des Typs, Typedie ich zur Laufzeit basierend auf einem an den Konstruktor übergebenen Aufzählungswert festgelegt habe, und stellt mir die relevanten Typen zur Verfügung, die ich dann in der MakeGenericMethod verwende.

Nochmals vielen Dank für das Hervorheben dieses @Bevan.

Thierry
quelle
Die Argumente für MakeGenericMethod()das Schlüsselwort params, damit Sie kein Array erstellen müssen. Sie müssen auch keine Instanzen erstellen, um die Typen zu erhalten - dies methodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject))würde ausreichen.
Bevan
0

Inspiriert von der Antwort von Enigmativity - nehmen wir an, Sie haben zwei (oder mehr) Klassen wie

public class Bar { }
public class Square { }

und Sie möchten die Methode Foo<T>mit Barund aufrufen Square, die als deklariert ist

public class myClass
{
    public void Foo<T>(T item)
    {
        Console.WriteLine(typeof(T).Name);
    }
}

Dann können Sie eine Erweiterungsmethode implementieren wie:

public static class Extension
{
    public static void InvokeFoo<T>(this T t)
    {
        var fooMethod = typeof(myClass).GetMethod("Foo");
        var tType = typeof(T);
        var fooTMethod = fooMethod.MakeGenericMethod(new[] { tType });
        fooTMethod.Invoke(new myClass(), new object[] { t });
    }
}

Mit diesem können Sie einfach Foowie folgt aufrufen :

var objSquare = new Square();
objSquare.InvokeFoo();

var objBar = new Bar();
objBar.InvokeFoo();

das funktioniert für jede Klasse. In diesem Fall wird Folgendes ausgegeben:

Square
Bar

Matt
quelle