Nach den Beispielen auf this post und its follow-up question, versuche ich Feld Getter/Setter mit kompilierten Ausdrücke zu erstellen.Feld Getter/Setter mit Ausdrucksbaum in der Basisklasse
Der Getter funktioniert einfach großartig, aber ich bin fest der Setter, wie ich den Setter, um jede Art von Feldern zuweisen müssen.
Hier meine Setter-Aktion Bauer:
public static Action<T1, T2> GetFieldSetter<T1, T2>(this FieldInfo fieldInfo) {
if (typeof(T1) != fieldInfo.DeclaringType && !typeof(T1).IsSubclassOf(fieldInfo.DeclaringType)) {
throw new ArgumentException();
}
ParameterExpression targetExp = Expression.Parameter(typeof(T1), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(T2), "value");
//
// Expression.Property can be used here as well
MemberExpression fieldExp = Expression.Field(targetExp, fieldInfo);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);
//
return Expression.Lambda<Action<T1, T2>> (assignExp, targetExp, valueExp).Compile();
}
Nun speichere ich die allgemeinen Setter in einen Cache-Liste (weil natürlich jedes Mal die Setter-Gebäude ist ein Leistungskiller), wo ich warf sie als einfach "Objekte":
// initialization of the setters dictionary
Dictionary<string, object> setters = new Dictionary(string, object)();
Dictionary<string, FieldInfo> fldInfos = new Dictionary(string, FieldInfo)();
FieldInfo f = this.GetType().GetField("my_int_field");
setters.Add(f.Name, GetFieldSetter<object, int>(f);
fldInfos.Add(f.Name, f);
//
f = this.GetType().GetField("my_string_field");
setters.Add(f.Name, GetFieldSetter<object, string>(f);
fldInfos.Add(f.Name, f);
Jetzt versuche ich so einen Feldwert zu setzen:
void setFieldValue(string fieldName, object value) {
var setterAction = setters[fieldName];
// TODO: now the problem => how do I invoke "setterAction" with
// object and fldInfos[fieldName] as parameters...?
}
I Ich könnte einfach eine generische Methode aufrufen und jedes Mal umwandeln, aber ich mache mir Sorgen um den Leistungsaufwand ... Irgendwelche Vorschläge?
- EDITED ANTWORT Basierend auf Mr Anderson's answer, habe ich ein kleines Testprogramm, das direkt den Wert vergleicht Einstellung zwischengespeichert Reflexion (wo Fieldinfo des zwischengespeichert werden) und die im Cache gespeicherten Multi-Typ-Code. Ich verwende Objektvererbung mit bis zu 3 Vererbungsebenen (ObjectC : ObjectB : ObjectA
).
Full code is of the example can be found here.
Einzel Iteration des Tests folgende Ausgabe ergibt:
-------------------------
--- OBJECT A ---
-------------------------
Set direct: 0.0036 ms
Set reflection: 2.319 ms
Set ref.Emit: 1.8186 ms
Set Accessor: 4.3622 ms
-------------------------
--- OBJECT B ---
-------------------------
Set direct: 0.0004 ms
Set reflection: 0.1179 ms
Set ref.Emit: 1.2197 ms
Set Accessor: 2.8819 ms
-------------------------
--- OBJECT C ---
-------------------------
Set direct: 0.0024 ms
Set reflection: 0.1106 ms
Set ref.Emit: 1.1577 ms
Set Accessor: 2.9451 ms
Natürlich ist dies zeigt einfach die Kosten für die Objekte zu schaffen - das ist uns die Offset der Erstellung der im Cache gespeicherten Versionen messen kann der Reflexion und der Ausdrücke.
Als nächstes wollen wir mal 1.000.000 laufen:
-------------------------
--- OBJECT A ---
-------------------------
Set direct: 33.2744 ms
Set reflection: 1259.9551 ms
Set ref.Emit: 531.0168 ms
Set Accessor: 505.5682 ms
-------------------------
--- OBJECT B ---
-------------------------
Set direct: 38.7921 ms
Set reflection: 2584.2972 ms
Set ref.Emit: 971.773 ms
Set Accessor: 901.7656 ms
-------------------------
--- OBJECT C ---
-------------------------
Set direct: 40.3942 ms
Set reflection: 3796.3436 ms
Set ref.Emit: 1510.1819 ms
Set Accessor: 1469.4459 ms
Aus Gründen der Vollständigkeit halber: ich den Aufruf der „set“ -Methode entfernt, um die Kosten des Erhaltens der Setter (FieldInfo
für das Reflexionsverfahren zu markieren , Action<object, object>
für den Ausdruck case). Hier die Ergebnisse:
-------------------------
--- OBJECT A ---
-------------------------
Set direct: 3.6849 ms
Set reflection: 44.5447 ms
Set ref.Emit: 47.1925 ms
Set Accessor: 49.2954 ms
-------------------------
--- OBJECT B ---
-------------------------
Set direct: 4.1016 ms
Set reflection: 76.6444 ms
Set ref.Emit: 79.4697 ms
Set Accessor: 83.3695 ms
-------------------------
--- OBJECT C ---
-------------------------
Set direct: 4.2907 ms
Set reflection: 128.5679 ms
Set ref.Emit: 126.6639 ms
Set Accessor: 132.5919 ms
HINWEIS: Zeit Anstieg ist hier nicht aufgrund der Tatsache, dass die Zugriffszeiten langsamer sind für größere Wörterbücher (wie sie O(1)
Zugriffszeiten), aber aufgrund der Tatsache, dass die Anzahl der Male, die wir Zugriff darauf wird erhöht (4 mal pro Iteration für ObjectA
, 8 für ObjectB
, 12 für ObjectC
) ... Wie man sieht, macht hier nur der Erstellungsoffset einen Unterschied (was zu erwarten ist).
Fazit: Wir haben die Leistung um einen Faktor von 2 oder mehr verbessert, aber wir sind immer noch weit von der Leistung des direkten Feldsatzes entfernt ... Das Auffinden des richtigen Setter in der Liste entspricht gut 10% der Zeit .
Ich werde mit Ausdrucksbäumen anstelle von Reflection.Emit versuchen, um zu sehen, ob wir die Lücke weiter reduzieren können ... Jeder Kommentar ist mehr als willkommen.
EDIT 2 I addierten Ergebnisse der Ansatz unter Verwendung eines generischen "Accessor"-Klasse als durch Eli Arbel auf this post vorgeschlagen.
„Besorgt über die Leistung“ nicht ganz es geschnitten. Testen Sie es, sehen Sie, ob es gut genug funktioniert, und entscheiden Sie sich danach. Ich sehe keinen Grund, warum die Verwendung einer generischen Methode schlechter wäre als Ihre derzeitige Vorgehensweise. – Luaan
Ich verwendete diesen Ansatz (Ausdrücke), dann entdeckte ich 'System.Reflection.Emit.DyanamicMethod' ist viel einfacher. –
Ich denke, die Dynamische Laufzeit speichert auch solche Sachen. Ich glaube nicht, dass es so viel langsamer würde. – MBoros