|
» myCSharp.de Diskussionsforum |
|
|
|
Autor
 |
|
|
oder: Erzeugung dynamischer Typen als Ersatz für GetValue/ SetValue mittels Reflection für häufige, performanzkritische Feld-/Propertyzugriffe.
Beschreibung:
Relektion ist toll 
. Mit Reflektion kann man z.B. die Felder und Properties von Typen auslesen und belegen. Ein Beispiel ist z.B. ein O/R-Mapper, der die Werte einer Datenbankzeile ausliest und entsprechend des Spaltennamens Felder/Properties eines Typs mit den jeweiligen Werten belegt.
Dabei werden jedoch oft mehrere hundert oder tausend Objekte erzeugt. Und Reflektion ist langsam. Deshalb bietet es sich an, dynamisch einen Typen zu erzeugen, der die entsprechenden Felder/Properties nicht mehr via Reflection-API liest/setzt, sondern dies quasi mit einem direkten Verweis macht. Die Grundidee ist aus CodeProject: Fast Dynamic Property Access, die hier gezeigte Klasse ist aber noch ergänzt um Typkonvertierung für den Anwendungsfall Einfacher Tabellen Mapper . Zusätzlich werden Felder unterstützt. Da Microsoft so genial war und den Begriff "(Klassen-)Attribute" überladen hat, heißt meine Klasse "DynamicFieldAccessor" (Properties sind auch nur abstrahierte Felder...).
Features:- Lesen und Schreiben von Feldern und Properties
- auch protected Felder/Properties können gelesen/geschrieben werden
- Unterstützung von Enums und Nullable-Types
- (normales) Lesen und Schreiben bis etwa 50x so schnell als Reflection
- unterstützt Typkonvertierung beim Schreiben (optional)
- beim Schreiben mit Typkonvertierung sogar schneller als direkter Zugriff
Beispiel:
C#-Code: |
public class TestClass<T>
{
public T Value { get; set; }
}
|
C#-Code: |
TestClass<int> testClass = new TestClass<int>();
int value = testClass.Value;
testClass.Value = value;
PropertyInfo propertyInfo = typeof(TestClass<int>).GetProperty("Value");
value = (int)propertyInfo.GetValue(testClass);
propertyInfo.SetValue(testClass,value);
propertyInfo = typeof(TestClass<int>).GetProperty("Value");
IDynamicFieldAccessor fieldAccessor = DynamicFieldAccessor.Create(propertyInfo,false);
value = fieldAccessor.Get(testClass);
fieldAccessor.Set(testClass,value);
|
DynamicFieldAccessor.Create(...) erzeugt dabei je nach Property-/Feld dynamisch einen neuen Typ (hier eine Klasse), die den Property/Feldzugriff quasi direkt durchführt. Im Folgenden sieht man für verschiedene Anwendungsfälle den C#-Code, der dem erzeugten IL-Code/dynamischen Typen entspricht: - IDynamicFieldAccessor fieldAccessor = DynamicFieldAccessor.Create(typeof(TestClass<string>).GetProperty ("Value"),false);
C#-Code: |
public class TestClass_SystemString_Value : IDynamicFieldAccessor
{
public virtual object Get(object target)
{
return ((TestClass<string>) target).Value;
}
public virtual void Set(object target, object value)
{
((TestClass<string>) target).Value = (string) value;
}
}
|
- IDynamicFieldAccessor fieldAccessor = DynamicFieldAccessor.Create(typeof(TestClass<int>).GetProperty ("Value"),true);
C#-Code: |
public class TestClass_Int_Value_TypeChange : IDynamicFieldAccessor
{
public object Get(object target)
{
return ((TestClass<int>) target).Value;
}
public void Set(object target, object value)
{
if ((value is int) || typeof(int).IsAssignableFrom(value.GetType()))
((TestClass<int>) target).Category = (int) value;
else
((TestClass<int>) target).Category = Convert.ToInt32(value);
}
}
|
- IDynamicFieldAccessor fieldAccessor = DynamicFieldAccessor.Create(typeof(TestClass<TestEnum>).GetProperty ("Value"),true);
C#-Code: |
public class TestClass_TestEnum_Value : IFieldAccessor
{
public virtual object Get(object target)
{
return ((TestClass<TestEnum>) target).Value;
}
public virtual void Set(object target, object value)
{
if (value is int)
((TestClass<TestEnum>) target).Value = (TestEnum) value;
else
((TestClass<TestEnum>) target).Value = (TestEnum) Enum.Parse(typeof(TestEnum), value.ToString());
}
}
|
- IDynamicFieldAccessor fieldAccessor = DynamicFieldAccessor.Create(typeof(TestClass<int?>).GetProperty ("Value"),true);
C#-Code: |
public class TestClass_SystemNullableSystemInt_Value : IDynamicFieldAccessor
{
public virtual object Get(object target)
{
return ((TestClass<int?>) target).Value;
}
public virtual void Set(object target, object value)
{
if ((value is int?) || typeof(int?).IsAssignableFrom(value.GetType()))
((TestClass<int?>) target).Value = (int?) value;
else
((TestClass<int?>) target).Value = (int?) Convert.ChangeType(value, Nullable.GetUnderlyingType(typeof(int?)));
}
}
|
Performanzvergleich:
Teste System.Int32 mit 500000 Wiederholungen
Getter:
direkter Zugriff 19
DynamicFieldAccessor 28 #
FastDynamicPropertyAccessor 47
Reflection/PropertyInfo.GetValue 1569
Setter (ohne Typkovertierungsprüfung):
direkter Zugriff 70
DynamicFieldAccessor 31 #
FastDynamicPropertyAccessor 43
Reflection/PropertyInfo.SetValue 1971
Setter (mit Typkonvertierungprüfung,ohne Typänderung):
direkter Zugriff 55
DynamicFieldAccessor 34 #
FastDynamicPropertyAccessor 70
Reflection/PropertyInfo.SetValue 2006
Setter (mit Typkonvertierungprüfung,mit Typänderung):
direkter Zugriff 771
DynamicFieldAccessor 394 #
FastDynamicPropertyAccessor 763
Reflection/PropertyInfo.SetValue 2947
Teste System.String mit 500000 Wiederholungen
Getter:
direkter Zugriff 17
DynamicFieldAccessor 31 #
FastDynamicPropertyAccessor 44
Reflection/PropertyInfo.GetValue 1439
Setter (ohne Typkovertierungsprüfung):
direkter Zugriff 35
DynamicFieldAccessor 41 #
FastDynamicPropertyAccessor 47
Reflection/PropertyInfo.SetValue 1835
Setter (mit Typkonvertierungprüfung,ohne Typänderung):
direkter Zugriff 41
DynamicFieldAccessor 24 #
FastDynamicPropertyAccessor 62
Reflection/PropertyInfo.SetValue 1893
Setter (mit Typkonvertierungprüfung,ohne Typänderung):
direkter Zugriff 51
DynamicFieldAccessor 28 #
FastDynamicPropertyAccessor 64
Reflection/PropertyInfo.SetValue 1877
Teste System.Nullable`1[[System.Int32]] mit 500000 Wiederholungen
Getter:
direkter Zugriff 23
DynamicFieldAccessor 474 #
FastDynamicPropertyAccessor 587
Reflection/PropertyInfo.GetValue 2323
Setter (ohne Typkovertierungsprüfung):
direkter Zugriff 286
DynamicFieldAccessor 730 #
FastDynamicPropertyAccessor 781
Reflection/PropertyInfo.SetValue 3067
Setter (mit Typkonvertierungprüfung,mit Typänderung):
direkter Zugriff 463
DynamicFieldAccessor 258 #
FastDynamicPropertyAccessor 658
Reflection/PropertyInfo.SetValue 2880
Setter (mit Typkonvertierungprüfung,mit Typänderung):
direkter Zugriff 2907
DynamicFieldAccessor 2154 #
FastDynamicPropertyAccessor 2880
Reflection/PropertyInfo.SetValue 6071
Code:
Und nun endlich der Code:
C#-Code: |
public interface IDynamicFieldAccessor
{
void Set(object target,object value);
object Get(object target);
}
|
C#-Code: |
public abstract class DynamicFieldAccessor
{
private static Dictionary<bool,Dictionary<MemberInfo,IDynamicFieldAccessor>> cache = new Dictionary<bool,Dictionary<MemberInfo,IDynamicFieldAccessor>>();
private static readonly MethodInfo typeGetTypeFromHandleMethodInfo = typeof(Type).GetMethod("GetTypeFromHandle");
private static readonly MethodInfo objectGetTypeMethodInfo = typeof(object).GetMethod("GetType");
private static readonly MethodInfo typeIsAssignableFromMethodInfo = typeof(Type).GetMethod("IsAssignableFrom");
private static readonly MethodInfo convertChangeTypeMethodInfo = typeof(Convert).GetMethod("ChangeType",new Type[]{typeof(object),typeof(Type)});
private static readonly MethodInfo objectToStringMethodInfo = typeof(object).GetMethod("ToString");
private static readonly MethodInfo enumParseMethodInfo = typeof(Enum).GetMethod("Parse",new Type[]{typeof(Type),typeof(string)});
private static readonly MethodInfo nullableGetUnderlyingTypeMethodInfo = typeof(Nullable).GetMethod("GetUnderlyingType");
static DynamicFieldAccessor()
{
DynamicFieldAccessor.cache.Add(true,new Dictionary<MemberInfo,IDynamicFieldAccessor>());
DynamicFieldAccessor.cache.Add(false,new Dictionary<MemberInfo,IDynamicFieldAccessor>());
}
public static IDynamicFieldAccessor Create(MemberInfo memberInfo,bool enableSetterTypeChange)
{
if (!(memberInfo is PropertyInfo || memberInfo is FieldInfo))
throw new ArgumentException(memberInfo+" ist kein Feld oder Property.");
else
if (DynamicFieldAccessor.cache[enableSetterTypeChange].ContainsKey(memberInfo))
return DynamicFieldAccessor.cache[enableSetterTypeChange][memberInfo];
else
{
IDynamicFieldAccessor fieldAccessor = DynamicFieldAccessor.create(memberInfo,enableSetterTypeChange);
DynamicFieldAccessor.cache[enableSetterTypeChange].Add(memberInfo,fieldAccessor);
return fieldAccessor;
}
}
private static IDynamicFieldAccessor create(MemberInfo memberInfo,bool enableSetterTypeChange)
{
#if DEBUG
string name = "[DynamicFieldAccessor]"+memberInfo.ReflectedType.Name+" "+Regex.Replace(memberInfo.ToString().Replace(" ","_"),"[^a-z^A-Z^_]","");
if (enableSetterTypeChange)
name += "(TypeChange)";
AssemblyName assemblyName = new AssemblyName(name);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (assemblyName,AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name,name+".dll");
#else
string name = Guid.NewGuid().ToString();
AssemblyName assemblyName = new AssemblyName(name);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (assemblyName,AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name);
#endif
TypeBuilder typeBuilder = (DynamicFieldAccessor.mustInherit(memberInfo)) ? moduleBuilder.DefineType(name,TypeAttributes.Public,memberInfo.ReflectedType) : moduleBuilder.DefineType(name,TypeAttributes.Public);
typeBuilder.AddInterfaceImplementation(typeof(IDynamicFieldAccessor));
DynamicFieldAccessor.implementGetMethod(typeBuilder,memberInfo);
DynamicFieldAccessor.implementSetMethod (typeBuilder,memberInfo,enableSetterTypeChange);
Type resultType = typeBuilder.CreateType();
#if DEBUG
assemblyBuilder.Save(name+".dll");
#endif
IDynamicFieldAccessor result = (IDynamicFieldAccessor)FormatterServices.GetUninitializedObject(resultType);
return result;
}
private static bool mustInherit(MemberInfo memberInfo)
{
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
FieldInfo fieldInfo = memberInfo as FieldInfo;
if (propertyInfo!=null)
{
MethodInfo getMethod = propertyInfo.GetGetMethod(true);
if (getMethod!=null)
{
if ((getMethod.Attributes & MethodAttributes.Family)!=MethodAttributes.Family)
throw new ArgumentException("'"+memberInfo+"' muss public oder protected sein.");
if ((getMethod.Attributes & MethodAttributes.Public)!=MethodAttributes.Public)
return true;
}
MethodInfo setMethod = propertyInfo.GetSetMethod(true);
if (setMethod!=null)
{
if ((setMethod.Attributes & MethodAttributes.Family)!=MethodAttributes.Family)
throw new ArgumentException("'"+memberInfo+"' muss public oder protected sein.");
if ((setMethod.Attributes & MethodAttributes.Public)!=MethodAttributes.Public)
return true;
}
}
else
{
if ((fieldInfo.Attributes & FieldAttributes.Family)!=FieldAttributes.Family)
throw new ArgumentException("'"+memberInfo+"' muss public oder protected sein.");
if ((fieldInfo.Attributes & FieldAttributes.Public)!=FieldAttributes.Public)
return true;
}
return false;
}
private static void implementGetMethod(TypeBuilder typeBuilder,MemberInfo memberInfo)
{
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
FieldInfo fieldInfo = memberInfo as FieldInfo;
MethodInfo methodInfo = typeof(IDynamicFieldAccessor).GetMethod("Get");
MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name,(MethodAttributes) (methodInfo.Attributes-MethodAttributes.Abstract),methodInfo.ReturnType,new Type[] { typeof(object) });
typeBuilder.DefineMethodOverride(methodBuilder,methodInfo);
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
if (propertyInfo!=null && propertyInfo.GetGetMethod(true)==null)
{
ilGenerator.Emit(OpCodes.Ldstr,"Property '"+memberInfo+"' hat keinen Getter.");
ilGenerator.Emit(OpCodes.Newobj,typeof(NotSupportedException).GetConstructor(new Type[] { typeof(string) }));
ilGenerator.Emit(OpCodes.Throw);
}
else
{
ilGenerator.DeclareLocal(typeof(object));
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Castclass,memberInfo.ReflectedType);
if (propertyInfo!=null)
ilGenerator.Emit(OpCodes.Callvirt,propertyInfo.GetGetMethod(true));
else
ilGenerator.Emit(OpCodes.Ldfld,fieldInfo);
Type memberType = (propertyInfo!=null) ? propertyInfo.PropertyType : fieldInfo.FieldType;
if (memberType.IsValueType)
ilGenerator.Emit(OpCodes.Box,memberType);
ilGenerator.Emit(OpCodes.Ret);
}
}
private static void implementSetMethod(TypeBuilder typeBuilder,MemberInfo memberInfo,bool changeType)
{
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
FieldInfo fieldInfo = memberInfo as FieldInfo;
Type memberType = (propertyInfo!=null) ? propertyInfo.PropertyType : fieldInfo.FieldType;
MethodInfo methodInfo = typeof(IDynamicFieldAccessor).GetMethod("Set");
MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name,(MethodAttributes) (methodInfo.Attributes-MethodAttributes.Abstract),methodInfo.ReturnType,new Type[] { typeof(object),typeof(object) });
typeBuilder.DefineMethodOverride(methodBuilder,methodInfo);
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
Label labelSetConverted = ilGenerator.DefineLabel();
Label labelSetDirect = ilGenerator.DefineLabel();
if (propertyInfo!=null && propertyInfo.GetSetMethod(true)==null)
{
ilGenerator.Emit(OpCodes.Ldstr,"Property '"+memberInfo+"' hat keinen Setter.");
ilGenerator.Emit(OpCodes.Newobj,typeof(NotSupportedException).GetConstructor(new Type[]{typeof(string)}));
ilGenerator.Emit(OpCodes.Throw);
}
else
{
if (changeType)
{
ilGenerator.DeclareLocal(typeof(bool));
ilGenerator.Emit(OpCodes.Ldarg_2);
ilGenerator.Emit(OpCodes.Isinst,(!memberType.IsEnum) ? memberType : typeof(int));
ilGenerator.Emit(OpCodes.Brtrue_S,labelSetDirect);
if (memberType.IsEnum)
ilGenerator.Emit(OpCodes.Br_S,labelSetConverted);
else
{
ilGenerator.Emit(OpCodes.Ldtoken,memberType);
ilGenerator.Emit(OpCodes.Call,typeGetTypeFromHandleMethodInfo);
ilGenerator.Emit(OpCodes.Ldarg_2);
ilGenerator.Emit(OpCodes.Callvirt,objectGetTypeMethodInfo);
ilGenerator.Emit(OpCodes.Callvirt,typeIsAssignableFromMethodInfo);
ilGenerator.Emit(OpCodes.Brfalse_S,labelSetConverted);
}
ilGenerator.MarkLabel(labelSetDirect);
}
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Castclass,memberInfo.ReflectedType);
ilGenerator.Emit(OpCodes.Ldarg_2);
ilGenerator.Emit(OpCodes.Unbox_Any,memberType);
if (propertyInfo!=null)
ilGenerator.Emit(OpCodes.Callvirt,propertyInfo.GetSetMethod(true));
else
ilGenerator.Emit(OpCodes.Stfld,fieldInfo);
ilGenerator.Emit(OpCodes.Ret);
if (changeType)
{
ilGenerator.MarkLabel(labelSetConverted);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Castclass,memberInfo.ReflectedType);
if (!memberType.IsEnum)
{
ilGenerator.Emit(OpCodes.Ldarg_2);
MethodInfo convertToMethodInfo = DynamicFieldAccessor.getConvertToMethodInfo(memberType);
if (convertToMethodInfo!=null)
ilGenerator.Emit(OpCodes.Call,convertToMethodInfo);
else
{
ilGenerator.Emit(OpCodes.Ldtoken,memberType);
ilGenerator.Emit(OpCodes.Call,typeGetTypeFromHandleMethodInfo);
if (memberType.IsGenericType && memberType.GetGenericTypeDefinition()==typeof(Nullable<>))
ilGenerator.Emit(OpCodes.Call,nullableGetUnderlyingTypeMethodInfo);
ilGenerator.Emit(OpCodes.Call,convertChangeTypeMethodInfo);
ilGenerator.Emit(OpCodes.Unbox_Any,memberType);
}
}
else
{
ilGenerator.Emit(OpCodes.Ldtoken,memberType);
ilGenerator.Emit(OpCodes.Call,typeGetTypeFromHandleMethodInfo);
ilGenerator.Emit(OpCodes.Ldarg_2);
ilGenerator.Emit(OpCodes.Callvirt,objectToStringMethodInfo);
ilGenerator.Emit(OpCodes.Call,enumParseMethodInfo);
ilGenerator.Emit(OpCodes.Unbox_Any,memberType);
}
if (propertyInfo!=null)
ilGenerator.Emit(OpCodes.Callvirt,propertyInfo.GetSetMethod(true));
else
ilGenerator.Emit(OpCodes.Stfld,fieldInfo);
ilGenerator.Emit(OpCodes.Ret);
}
}
}
private static MethodInfo getConvertToMethodInfo(Type targetType)
{
string methodName;
if (targetType==typeof(Boolean))
methodName = "ToBoolean";
else if (targetType==typeof(Byte))
methodName = "ToByte";
else if (targetType==typeof(Char))
methodName = "ToChar";
else if (targetType==typeof(DateTime))
methodName = "ToDateTime";
else if (targetType==typeof(Decimal))
methodName = "ToDecimal";
else if (targetType==typeof(Double))
methodName = "ToDouble";
else if (targetType==typeof(Int16))
methodName = "ToInt16";
else if (targetType==typeof(Int32))
methodName = "ToInt32";
else if (targetType==typeof(Int64))
methodName = "ToInt64";
else if (targetType==typeof(SByte))
methodName = "ToSByte";
else if (targetType==typeof(Single))
methodName = "ToSingle";
else if (targetType==typeof(String))
methodName = "ToString";
else if (targetType==typeof(UInt16))
methodName = "ToUInt16";
else if (targetType==typeof(UInt32))
methodName = "ToUInt32";
else if (targetType==typeof(UInt64))
methodName = "ToUInt64";
else
return null;
return typeof(Convert).GetMethod(methodName,new Type[]{typeof(object)});
}
}
|
Schlagwörter: DynamicFieldAccessor, Felder, Properties, Reflection, IL-Generator
letzte Änderung am Code: 27.03.2008
Dieser Beitrag wurde 12 mal editiert, zum letzten Mal von dN!3L am 16.12.2009 12:03.
|
|
17.03.2008 20:12
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
onlinegurke
myCSharp.de-Mitglied
Dabei seit: 15.01.2007
Beiträge: 779
Entwicklungsumgebung: VS2012 Ultimate Herkunft: Dresdner in Karlsruhe
|
|
Blöde Frage: Warum benutzt du nicht einfach System.Reflection.Emit.DynamicMethod? So hab ich's bei meinen Cheatmeisen gemacht (AntMe), die dann in der Lage waren sich selbst zu heilen und die Käfer für tot zu erklären
Aber Spaß beiseite, mit DynamicMethod geht das ganze viel einfacher, da du m.E. nach einfach an den bestehenden Typen zur Laufzeit quasi eine Methode dranhaengst. Der Zugriff erfolgt dann per Delegate, aber den kann man ja kapseln.
Und ein bisschen (konstruktive) Kritik hab ich auch noch übrig 
Du rufst Properties immer ueber callvirt auf, das ist aber nur bei virtuellen Properties notwendig.
|
|
18.03.2008 18:24
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Hallo,
Zitat von onlinegurke: |
Blöde Frage: Warum benutzt du nicht einfach System.Reflection.Emit.DynamicMethod? Aber Spaß beiseite, mit DynamicMethod geht das ganze viel einfacher, da du m.E. nach einfach an den bestehenden Typen zur Laufzeit quasi eine Methode dranhaengst. Der Zugriff erfolgt dann per Delegate, aber den kann man ja kapseln. |
Hm, am besten mache ich eine Liste: - Kannte ich nicht (woher kennst du dich damit aus?)
- Naja, "einfacher" werde ich es nicht mehr haben. Die Klasse ist fertig und wird eingesetzt
- Das "einfacher" würde ich auch erstmal nur auf die Programmierung beziehen. Denn von der Struktur her kann ich nicht erkennen, dass es einfacher, als die Klasse/das Interface ist (wäre eher noch zusätzlicher Aufwand)
- Es geht hier primär um Performanz. Ich würde einfach mal in den Raum stellen, dass so ein Delegate-Zugriff langsamer ist. Wenn ich mal ne freie Minute habe, werd ich mir DynamicMethod aber mal genauer angucken und testen.
- Summa summarum sehe ich als einzigen Vorteil, dass ich die Methode einfach in ein bestehendes Modul hänge und man so auch leichter Zugriff auf nichtöffentliches Zeugs hat (Was meine Lösung aber auch kann).
Zitat von onlinegurke: |
Und ein bisschen (konstruktive) Kritik hab ich auch noch übrig
Du rufst Properties immer ueber callvirt auf, das ist aber nur bei virtuellen Properties notwendig. |
Hm, ich hab "Reverse-Programming" betrieben. Der C#-Compiler hat das so ausgegeben. Performanzmäßig bringt ein Call aber auch nichts.
Gruß
dN!3L
|
|
19.03.2008 12:18
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Sehr interessant, werde das ganze gleich mal in meinen einfachen Tabellen Mapper integrieren. Damit dürfte ich Performanzmäßig noch einiges herausholen können :-).
Die DynamicMethod Variante habe ich mir auch schon angesehen.
Ich befürchte auch, dass diese Vorgehensweise langsamer ist.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von jnetrick am 20.03.2008 11:43.
|
|
20.03.2008 11:41
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
onlinegurke
myCSharp.de-Mitglied
Dabei seit: 15.01.2007
Beiträge: 779
Entwicklungsumgebung: VS2012 Ultimate Herkunft: Dresdner in Karlsruhe
|
|
Ich hab die Performance von DynamicMethod ehrlich gesagt noch nie getestet, kann mir aber nicht wirklich vorstellen, dass der Delegate-Zugriff soo viel langsamer ist als der Zugriff über Interface. Momentan kann ich's auch nicht überprüfen (Lüfter vom Laptop kaputt 
)
|
|
20.03.2008 12:15
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
feadur
myCSharp.de-Mitglied
Dabei seit: 11.03.2005
Beiträge: 722
Herkunft: Bonn
|
|
Das ist ne tolle Sache, ich habe mir direkt mal die DynamicMethod Variante zusammengebastelt.
Aber leider lohnt sich das ganze nur, wenn ich sehr oft ein und das selbe Property schreibe oder lese. Ansonsten ist der Aufwand zu groß, den Code zu erzeugen.
Wie auch immer, trotzdem ne gute Sache!
Gruß
|
|
20.03.2008 16:39
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Hallo,
Zitat von feadur: |
Das ist ne tolle Sache, ich habe mir direkt mal die DynamicMethod Variante zusammengebastelt. |
Könntest du mal bitte den Code posten (bzw. mit zukommen lassen)? Dann kann ich mal ein paar Performanztests machen (und muss nicht so viel vorbereiten...).
Zitat von feadur: |
Aber leider lohnt sich das ganze nur, wenn ich sehr oft ein und das selbe Property schreibe oder lese. |
Ja, genau dafür ist es doch gedacht.
Gruß
dN!3L
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von dN!3L am 20.03.2008 23:37.
|
|
20.03.2008 20:03
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
feadur
myCSharp.de-Mitglied
Dabei seit: 11.03.2005
Beiträge: 722
Herkunft: Bonn
|
|
Hier die DynamicMethod Lösung:
C#-Code: |
public class DynamicAccessor
{
delegate object Getter(object target);
delegate void Setter(object target, object value);
Getter _getter;
Setter _setter;
public DynamicAccessor(PropertyInfo propertyInfo)
{
_getter = EmitGetter(propertyInfo);
if (propertyInfo.CanWrite)
{
_setter = EmitSetter(propertyInfo);
}
}
Getter EmitGetter(PropertyInfo propertyInfo)
{
DynamicMethod dynMethod = new DynamicMethod("Get",
typeof(object),
new Type[]{typeof(object)},
propertyInfo.DeclaringType);
ILGenerator il = dynMethod.GetILGenerator();
MethodInfo getterMethod = propertyInfo.GetGetMethod();
il.DeclareLocal(typeof(object));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
il.EmitCall(OpCodes.Callvirt, getterMethod, null);
OpCode box = propertyInfo.PropertyType.IsValueType ?
OpCodes.Box :
OpCodes.Castclass;
il.Emit(box, propertyInfo.PropertyType);
il.Emit(OpCodes.Ret);
return (Getter)dynMethod.CreateDelegate(typeof(Getter));
}
Setter EmitSetter(PropertyInfo propertyInfo)
{
DynamicMethod dynMethod = new DynamicMethod("Set",
null,
new Type[]{typeof(object),
typeof(object)},
propertyInfo.DeclaringType);
ILGenerator il = dynMethod.GetILGenerator();
MethodInfo setter = propertyInfo.GetSetMethod();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
il.Emit(OpCodes.Ldarg_1);
OpCode box = propertyInfo.PropertyType.IsValueType ?
OpCodes.Unbox_Any :
OpCodes.Castclass;
il.Emit(box, propertyInfo.PropertyType);
il.EmitCall(OpCodes.Callvirt, setter, null);
il.Emit(OpCodes.Ret);
return (Setter)dynMethod.CreateDelegate(typeof(Setter));
}
public void Set(object target, object value)
{
if (_setter == null)
throw new NotSupportedException("Property is read-only");
_setter(target, value);
}
public object Get(object target)
{
return _getter(target);
}
}
|
Ich suche jetzt nach einer Möglichkeit, die Zieltypen im IL Code zu verändern, dann könnte man das ganze dynamisch für verschiedene Properties verwenden. DynamicILInfo könnte dabei helfen.
Hat jemand damit schon Erfahrungen gemacht?
|
|
22.03.2008 17:04
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
Zitat von feadur: |
Hier die DynamicMethod Lösung: |
Und hier die Performanzprüfungen:
Die erste DynamicMethod-Variante ist direkt das Getter-Delegate aufzurufen, die zweite ist die, die du gepostet hast (also quasi noch ein Proxy).
Man sieht, die DynamicMethod-Variante liegt nur ganz knapp hinter der DynamicType-Variante. Hätte ich nicht gedacht...
Teste System.Int32 mit 1000000 Wiederholungen
Getter:
direkter Zugriff 29
DynamicFieldAccessor 43
DynamicMethod Getter 46
DynamicMethodPropertyAccessor 45
FastDynamicPropertyAccessor 46
Reflection/PropertyInfo.GetValue 2058
Setter (ohne Typkovertierungsprüfung):
direkter Zugriff 27
DynamicFieldAccessor 43
DynamicMethod Setter 52
DynamicMethodPropertyAccessor 55
FastDynamicPropertyAccessor 56
Reflection/PropertyInfo.SetValue 2654
Teste System.String mit 1000000 Wiederholungen
Getter:
direkter Zugriff 17
DynamicFieldAccessor 35
DynamicMethod Getter 36
DynamicMethodPropertyAccessor 37
FastDynamicPropertyAccessor 39
Reflection/PropertyInfo.GetValue 1906
Setter (ohne Typkovertierungsprüfung):
direkter Zugriff 33
DynamicFieldAccessor 40
DynamicMethod Setter 42
DynamicMethodPropertyAccessor 46
FastDynamicPropertyAccessor 48
Reflection/PropertyInfo.SetValue 2443
Teste System.Nullable`1[[System.Int32]] mit 1000000 Wiederholungen
Getter:
direkter Zugriff 32
DynamicFieldAccessor 644
DynamicMethod Getter 641
DynamicMethodPropertyAccessor 630
FastDynamicPropertyAccessor 659
Reflection/PropertyInfo.GetValue 3157
Setter (ohne Typkovertierungsprüfung):
direkter Zugriff 324
DynamicFieldAccessor 945
DynamicMethod Setter 951
DynamicMethodPropertyAccessor 961
FastDynamicPropertyAccessor 988
Reflection/PropertyInfo.SetValue 4022
Zitat von feadur: |
Ich suche jetzt nach einer Möglichkeit, die Zieltypen im IL Code zu verändern, dann könnte man das ganze dynamisch für verschiedene Properties verwenden. |

Was willst du für Zieltypen verändern, und warum?
EDIT: (Folgebeitrag für diese Frage ist Properties dynamisch schneller schreiben als mit PropertyInfo.SetValue )
Gruß
dN!3L
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von dN!3L am 26.03.2008 09:03.
|
|
22.03.2008 18:55
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Zwischen diesen beiden Beiträgen liegen mehr als 5 Jahre. |
chrkon
myCSharp.de-Mitglied
Dabei seit: 22.01.2014
Beiträge: 1
Entwicklungsumgebung: VS2012
|
|
Hallo allerseits,
auch wenn dieser Beitrag schon älter ist, möchte ich noch eine Verbesserung beisteuern.
Der Zugriff auf Structs wie System.Windows.Media.Media3D.Point3D klappt mit der oben beschriebenen Dynamic Method Lösung nicht. Ich habe mir dafür folgenden Unit Test geschrieben:
C#-Code: |
[Test]
public void CheckStructSecondParameter()
{
var Pos = new System.Windows.Media.Media3D.Point3D(10.0,20.0,30.0);
var expected = 20.0;
var daSubProp = new DynamicAccessor(Pos.GetType().GetProperty("Y"));
var result = daSubProp.Get(Pos);
Assert.AreEqual(expected, result);
}
|
Dieser Test schlägt fehl! Eigenartig ist jedoch, dass die Variable 'result' den Wert 10.0 hat. Es wird also scheinbar die falsche Property ausgelesen. In diesem Fall passt es zur Property "X".
Wenn man nun statt der Point3D Struktur eine Point3D Klasse anlegt
C#-Code: |
public class myPoint3D
{
public myPoint3D(double x, double y, double z)
{
X = x; Y = y; Z = z;
}
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
|
und damit den gleichen Test durchführt
C#-Code: |
[Test]
public void CheckClassSecondParameter()
{
var Pos = new myPoint3D(10.0, 20.0, 30.0);
var expected = 20.0;
var daSubProp = new DynamicAccessor(Pos.GetType().GetProperty("Y"));
var result = daSubProp.Get(Pos);
Assert.AreEqual(expected, result);
}
|
funktioniert es. Diesmal ist der Test grün.
Bei Stackoverflow habe ich einen Hinweis gefunden: DynamicMethod returns incorrect value when property type is Int64
Ich habe daher die 'EmitGetter' Funktion folgendermaßen angepasst:
C#-Code: |
Getter EmitGetter(PropertyInfo propertyInfo)
{
DynamicMethod dynMethod = new DynamicMethod("Get",
typeof(object),
new Type[] { typeof(object) },
propertyInfo.DeclaringType);
ILGenerator il = dynMethod.GetILGenerator();
MethodInfo getterMethod = propertyInfo.GetGetMethod();
il.DeclareLocal(typeof(object));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
if (propertyInfo.PropertyType.IsValueType)
{
if (propertyInfo.ReflectedType.IsValueType)
{
il.Emit(OpCodes.Unbox, propertyInfo.ReflectedType);
}
il.EmitCall(OpCodes.Callvirt, getterMethod, null);
il.Emit(OpCodes.Box, propertyInfo.PropertyType);
}
else
{
il.EmitCall(OpCodes.Callvirt, getterMethod, null);
il.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
}
il.Emit(OpCodes.Ret);
return (Getter)dynMethod.CreateDelegate(typeof(Getter));
}
|
Mit dieser Änderung werden die beiden Tests erfolgreich abgeschlossen.
Da ich in meiner Software keinen schreibenden Zugriff benötige, habe ich mir die 'EmitSetter' Funktion nicht näher angesehen. Ich vermute aber, dass dort auch noch Anpassungen notwendig sind.
Viele Grüße,
Christof
|
|
22.01.2014 09:25
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
|