Frage

Meine Frage ist:

Wenn ich eine bauen will DynamicMethod Objekt, entsprechend a Konstruktorinfo.Invoke Rufen Sie an, welche Arten von IL muss ich implementieren, um mit allen (oder den meisten) Argumenten fertig zu werden, wenn ich garantieren kann, dass der richtige Typ und die Anzahl der Argumente vorgegeben werden, bevor ich den Anruf tätige?


Hintergrund

Ich bin auf meiner dritten Iteration meines IOC -Behälters und mache derzeit einige Profile, um herauszufinden, ob es Bereiche gibt, in denen ich leicht viel Zeit einstellen kann.

Eine Sache, die mir aufgefallen ist, ist, dass ich letztendlich einen Konstruktor, der aufgerufen wird, bei der Auflösung auf einen konkreten Typ verwendet wird Konstruktorinfo.Invoke, geben Sie eine Reihe von Argumenten vor, die ich ausgearbeitet habe.

Was mir aufgefallen ist, ist, dass die Invoke -Methode einiges an Overhead enthält und ich mich frage, ob die meisten davon nur unterschiedliche Implementierungen der gleichen Überprüfungen sind, die ich mache.

Zum Beispiel gibt es aufgrund des Konstruktor -Matching -Code, den ich habe, einen passenden Konstruktor für die vordefinierten Parameternamen, Typen und Werte, die ich übergeben habe Um wie die korrekte Anzahl von Argumenten in der richtigen Reihenfolge, den richtigen Typ und mit entsprechenden Werten fertig zu werden.

Bei einer Profiling -Sitzung, die eine Million Anrufe an meine Auflösungsmethode enthält und sie dann durch ein ersetzt DynamicMethod Implementierung, die den Aufruf nachahmt, war der Profilerstellungszeitpunkt wie folgt:

  • Konstruktorinfo.Invoke: 1973ms
  • DynamicMethod: 93 ms

Dies macht rund 20% der Gesamtlaufzeit dieser Profiling -Anwendung aus. Mit anderen Worten, indem ich den Konstruktorinfo ersetzen kann.

Ich denke, dies ist ziemlich umfangreich und garantiert genauer, wie viel Arbeit es sein würde, einen stabilen DynamicMethod -Generator für Konstruktoren in diesem Zusammenhang zu erstellen.

Die dynamische Methode würde also ein Objektarray aufnehmen und das konstruierte Objekt zurückgeben, und ich kenne bereits das betreffende Konstruktorinfo -Objekt.

Daher sieht es so aus, als würde die dynamische Methode aus dem folgenden IL bestehen:

l001:    ldarg.0      ; the object array containing the arguments
l002:    ldc.i4.0     ; the index of the first argument
l003:    ldelem.ref   ; get the value of the first argument
l004:    castclass T  ; cast to the right type of argument (only if not "Object")
(repeat l001-l004 for all parameters, l004 only for non-Object types,
 varying l002 constant from 0 and up for each index)
l005:    newobj ci    ; call the constructor
l006:    ret

Muss ich noch etwas berücksichtigen?

Beachten Sie, dass mir bewusst ist, dass das Erstellen von dynamischen Methoden wahrscheinlich nicht verfügbar ist, wenn die Anwendung im "reduzierten Zugriffsmodus" ausgeführt wird (manchmal gibt das Gehirn diese Begriffe einfach nicht auf), aber in diesem Fall kann ich das leicht erkennen und nur nur Aufrufen des ursprünglichen Konstruktors wie zuvor, mit dem Overhead und allem.

War es hilfreich?

Lösung

Für Werttypen sollte Schritt L004 sein l004: unbox.any T.

Der einfachste Weg, um die richtige IL zu finden, die Sie generieren müssen, besteht darin, zu untersuchen, was vom C# Compiler mit einem Testcode generiert wird.

static void Test(object[] args)
{
  TestTarget((string)args[0], (int)args[1], (DateTime?)args[2]);
}

static void TestTarget(string s, int i, DateTime? dt){}

kompiliert zu:

L_0000: ldarg.0 
L_0001: ldc.i4.0 
L_0002: ldelem.ref 
L_0003: castclass string
L_0008: ldarg.0 
L_0009: ldc.i4.1 
L_000a: ldelem.ref 
L_000b: unbox.any int32
L_0010: ldarg.0 
L_0011: ldc.i4.2 
L_0012: ldelem.ref 
L_0013: unbox.any [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>
L_0018: call void Program::TestTarget(string, int32, valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>)
L_001d: ret 

Andere Tipps

Es stehen Bibliotheken zur Verfügung, um es zu erleichtern (und schneller), mit Reflexion zu arbeiten. Zum Beispiel, Schneller Kann IL generieren, um einen Konstruktor aufzurufen - lediglich müssen Sie die Argumente übergeben, die Sie für den Konstruktor verwenden möchten.

// note: class must have constructor with (int,string,string) signature
object obj = someType.CreateInstance( new { id=1, name="jens", foo="bar" } );

Die Bibliothek ist auch in der Lage, einen geeigneten Konstruktor aufzurufen, falls Sie keine Reihe von Parametern haben, die genau mit einem Konstruktor übereinstimmen.

// try to map id, name and foo to constructor parameters
// allows changing the order and permit fallback to setting fields/properties
// e.g. might result in call to ctor(string,string) and set field "id"
object obj = someType.TryCreateInstance( new { id=1, name="jens", foo="bar" } );

Haftungsausschluss: Ich bin an diesem Projekt als Mitwirkender beteiligt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top