Beschreibung:
Relektion ist toll :D. 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:
public class TestClass<T>
{
public T Value { get; set; }
}
TestClass<int> testClass = new TestClass<int>();
// direkter Zugriff
int value = testClass.Value;
testClass.Value = value;
// Zugriff mit Reflection-API
PropertyInfo propertyInfo = typeof(TestClass<int>).GetProperty("Value");
value = (int)propertyInfo.GetValue(testClass);
propertyInfo.SetValue(testClass,value);
// Zugriff mit DynamicFieldAccessor
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);
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);
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);
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);
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:
/// <summary>
/// Ermöglicht den schnellen Zugriff (ohne Reflection) auf Felder/Properties
/// </summary>
public interface IDynamicFieldAccessor
{
/// <summary>
/// Ersetzt das Property/Feld, das durch diese Instanz repräsentiert wird, am übergeben Zielobjekt durch den übergeben Wert
/// </summary>
/// <param name="target">das Zielobjekt, dem das Feld/Property angehört</param>
/// <param name="value">der Wert, der gesetzt werden soll</param>
void Set(object target,object value);
/// <summary>
/// Liest das Property/Feld, das durch diese Instanz repräsentiert wird, vom übergeben Zielobjekt aus
/// </summary>
/// <param name="target">das Zielobjekt, dem das Feld/Property angehört</param>
/// <returns>den Wert des Properties/Feldes, das durch diese Instantz repräsentiert wird</returns>
object Get(object target);
}
/*using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;*/
/// <summary>
/// Stellt Methoden bereit, um Objekte zu erzeugen, die den schnellen Zugriff (ohne Reflection) auf Felder/Properties ermöglichen
/// </summary>
public abstract class DynamicFieldAccessor
{
// Cache zum Speichern der bereits erzeugten "dynamischen Feldzugreifer"
private static Dictionary<bool,Dictionary<MemberInfo,IDynamicFieldAccessor>> cache = new Dictionary<bool,Dictionary<MemberInfo,IDynamicFieldAccessor>>();
// MethodInfos, die oft benutzt werden
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");
/// <summary>
/// Konstruktor
/// </summary>
static DynamicFieldAccessor()
{
// Felder im Cache anlegen
DynamicFieldAccessor.cache.Add(true,new Dictionary<MemberInfo,IDynamicFieldAccessor>());
DynamicFieldAccessor.cache.Add(false,new Dictionary<MemberInfo,IDynamicFieldAccessor>());
}
/// <summary>
/// Erzeugt eine neue IDynamicFieldAccessor-Instanz
/// </summary>
/// <param name="memberInfo">das Feld/Property, auf das zugegriffen werden soll</param>
/// <param name="enableSetterTypeChange">true, falls eine (eventuelle) Typkonvertierung beim Setzen von Werten vorgenommen werden soll</param>
/// <returns>eine neue IDynamicFieldAccessor-Instanz zum Zugriff auf das übergebene Feld/Property</returns>
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
// Instanz aus dem Cache zurückgeben
if (DynamicFieldAccessor.cache[enableSetterTypeChange].ContainsKey(memberInfo))
return DynamicFieldAccessor.cache[enableSetterTypeChange][memberInfo];
// neue Instanz erzeugen und im Cache speichern
else
{
IDynamicFieldAccessor fieldAccessor = DynamicFieldAccessor.create(memberInfo,enableSetterTypeChange);
DynamicFieldAccessor.cache[enableSetterTypeChange].Add(memberInfo,fieldAccessor);
return fieldAccessor;
}
}
/// <summary>
/// Erzeugt eine neue IDynamicFieldAccessor-Instanz
/// </summary>
/// <param name="memberInfo">das Feld/Property, auf das zugegriffen werden soll</param>
/// <param name="enableSetterTypeChange">true, falls eine (eventuelle) Typkonvertierung beim Setzen von Werten vorgenommen werden soll</param>
/// <returns>eine neue IDynamicFieldAccessor-Instanz zum Zugriff auf das übergebene Feld/Property</returns>
private static IDynamicFieldAccessor create(MemberInfo memberInfo,bool enableSetterTypeChange)
{
// Pipeline AssemblyBuilder->ModuleBuilder->TypeBuilder
#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
// einen neuen TypeBuilder erzeugen. Falls das Feld/Property nicht public ist, muss vom reflektierten Typ geerbt werden, damit Zugriff erhalten werden kann
TypeBuilder typeBuilder = (DynamicFieldAccessor.mustInherit(memberInfo)) ? moduleBuilder.DefineType(name,TypeAttributes.Public,memberInfo.ReflectedType) : moduleBuilder.DefineType(name,TypeAttributes.Public);
// der neue Typ implementiert die Schnittstelle IDynamicFieldAccessor; die hat eine Methode "Get" und eine Methode "Set", welche implementiert werden müssen
typeBuilder.AddInterfaceImplementation(typeof(IDynamicFieldAccessor));
DynamicFieldAccessor.implementGetMethod(typeBuilder,memberInfo);
DynamicFieldAccessor.implementSetMethod (typeBuilder,memberInfo,enableSetterTypeChange);
// den Typen erzeugen
Type resultType = typeBuilder.CreateType();
#if DEBUG
// die Assembly auf die Platte speichern, damit man den produzierten IL-/C#-Code angucken kann
assemblyBuilder.Save(name+".dll");
#endif
// neue Instanz erzeugen -> dabei wird Konstruktor nicht aufgerufen (braucht nicht; hätte bei Spezialisierungen, um an protected Member zu kommen, auch Nebeneffekte)
IDynamicFieldAccessor result = (IDynamicFieldAccessor)FormatterServices.GetUninitializedObject(resultType);
return result;
}
/// <summary>
/// Prüft, ob ein neuer Typ vom Typ, der das Property/Feld enthält, ableiten muss, um an das Property/Feld zu kommen, da dies nicht public ist
/// </summary>
/// <param name="memberInfo">der Member, der untersucht werden soll</param>
/// <returns>true, falls abgeleitet werden muss</returns>
private static bool mustInherit(MemberInfo memberInfo)
{
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
FieldInfo fieldInfo = memberInfo as FieldInfo;
// Es muss abgeleitet werden, wenn Getter oder Setter des Properties oder das Feld nicht public ist.
// Getter/Setter/Feld müssen natürlich auch protected sein
// wenn es ein Property ist
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;
}
}
// wenn es ein Feld ist
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;
}
/// <summary>
/// Implementiert die "object Get(object target)"-Methode von IDynamicFieldAccessor
/// </summary>
/// <param name="typeBuilder">der zu verwendende TypeBuilder</param>
/// <param name="memberInfo">das Feld/Property, auf das ausgelesen werden soll</param>
private static void implementGetMethod(TypeBuilder typeBuilder,MemberInfo memberInfo)
{
PropertyInfo propertyInfo = memberInfo as PropertyInfo;
FieldInfo fieldInfo = memberInfo as FieldInfo;
// neue Methode erstellen und die Methode aus der Schnittstellendeklaration damit überschreiben
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);
// IL-Generator erzeugen
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
if (propertyInfo!=null && propertyInfo.GetGetMethod(true)==null)
{
// wenn es ein Property ist und kein Getter vorhanden ist, Exception werfen
ilGenerator.Emit(OpCodes.Ldstr,"Property '"+memberInfo+"' hat keinen Getter.");
ilGenerator.Emit(OpCodes.Newobj,typeof(NotSupportedException).GetConstructor(new Type[] { typeof(string) }));
ilGenerator.Emit(OpCodes.Throw);
}
// Property-/Feldzugriff implementieren
else
{
// lokale Variable
ilGenerator.DeclareLocal(typeof(object));
// übergebenes Objekt laden und zum casten
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Castclass,memberInfo.ReflectedType);
if (propertyInfo!=null)
// Property auslesen
ilGenerator.Emit(OpCodes.Callvirt,propertyInfo.GetGetMethod(true));
else
// Feld auslesen
ilGenerator.Emit(OpCodes.Ldfld,fieldInfo);
// den gelesenen Property-/Feldwert boxen
Type memberType = (propertyInfo!=null) ? propertyInfo.PropertyType : fieldInfo.FieldType;
if (memberType.IsValueType)
ilGenerator.Emit(OpCodes.Box,memberType);
// gelesenen Property-/Feldwert zurückgeben
ilGenerator.Emit(OpCodes.Ret);
}
}
/// <summary>
/// Implementiert die "void Set(object target,object value);"-Methode von IDynamicFieldAccessor
/// </summary>
/// <param name="typeBuilder">der zu verwendende TypeBuilder</param>
/// <param name="memberInfo">das Feld/Property, auf das gesetzt werden soll</param>
/// <param name="changeType">true, falls eine (eventuelle) Typkonvertierung beim Setzen von Werten vorgenommen werden soll</param>
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; // Zieltyp
// neue Methode erstellen und die Methode aus der Schnittstellendeklaration damit überschreiben
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);
// IL-Generator erzeugen
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
// Sprungziele definieren
Label labelSetConverted = ilGenerator.DefineLabel();
Label labelSetDirect = ilGenerator.DefineLabel();
if (propertyInfo!=null && propertyInfo.GetSetMethod(true)==null)
{
// wenn es ein Property ist und kein Setter vorhanden ist, Exception werfen
ilGenerator.Emit(OpCodes.Ldstr,"Property '"+memberInfo+"' hat keinen Setter.");
ilGenerator.Emit(OpCodes.Newobj,typeof(NotSupportedException).GetConstructor(new Type[]{typeof(string)}));
ilGenerator.Emit(OpCodes.Throw);
}
// Property-/Feldzugriff implementieren
else
{
// wenn gewünscht, die TypPRÜFUNG implementieren
if (changeType)
{
// lokale Variable
ilGenerator.DeclareLocal(typeof(bool));
// den zu setzende Wert laden; {value is MemberType}-Vergleich machen; falls erfolgreich, zum direkt ohne Konvertieren setzen gehen
ilGenerator.Emit(OpCodes.Ldarg_2);
ilGenerator.Emit(OpCodes.Isinst,(!memberType.IsEnum) ? memberType : typeof(int));
ilGenerator.Emit(OpCodes.Brtrue_S,labelSetDirect);
// is-Vergleich ist nicht erfolgreich: bei Enumtypen zum Konvertieren gehen...
if (memberType.IsEnum)
ilGenerator.Emit(OpCodes.Br_S,labelSetConverted);
// ... ansonsten noch Testen, ob der Wert nicht doch zuweisbar ist
else
{
// {if (typeof(MemberType).IsAssignableFrom(value.GetType()))}
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);
// wenn der Test nicht erfolgreich war, zum Konvertieren gehen...
ilGenerator.Emit(OpCodes.Brfalse_S,labelSetConverted);
// ... ansonsten kommt jetzt folgend die Zuweisung ohne Konvertieren
}
ilGenerator.MarkLabel(labelSetDirect);
}
// Zielobjekt laden; dieses casten; Wert laden; unboxen
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)
// Property setzen
ilGenerator.Emit(OpCodes.Callvirt,propertyInfo.GetSetMethod(true));
else
// Feld setzen
ilGenerator.Emit(OpCodes.Stfld,fieldInfo);
// fertig
ilGenerator.Emit(OpCodes.Ret);
// falls gewünscht, hier der Teil zum Konvertieren
if (changeType)
{
ilGenerator.MarkLabel(labelSetConverted);
// Zielobjekt laden und casten
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Castclass,memberInfo.ReflectedType);
if (!memberType.IsEnum)
{
// value laden
ilGenerator.Emit(OpCodes.Ldarg_2);
MethodInfo convertToMethodInfo = DynamicFieldAccessor.getConvertToMethodInfo(memberType);
if (convertToMethodInfo!=null)
// {Convert.ToXY(value)}
ilGenerator.Emit(OpCodes.Call,convertToMethodInfo);
else
{
// {Convert.ChangeType(value,typeof(MemberType))}
// Zieltyp laden
ilGenerator.Emit(OpCodes.Ldtoken,memberType);
ilGenerator.Emit(OpCodes.Call,typeGetTypeFromHandleMethodInfo);
if (memberType.IsGenericType && memberType.GetGenericTypeDefinition()==typeof(Nullable<>))
// {Convert.ChangeType(value,Nullable.GetUnderlyingType(typeof(MemberType)))}
// GetUnderlyingType aufrufen
ilGenerator.Emit(OpCodes.Call,nullableGetUnderlyingTypeMethodInfo);
// .ToXY/.ChangeType aufrufen
ilGenerator.Emit(OpCodes.Call,convertChangeTypeMethodInfo);
ilGenerator.Emit(OpCodes.Unbox_Any,memberType);
}
}
// Enums
else
{
// Enum-Zieltyp laden
ilGenerator.Emit(OpCodes.Ldtoken,memberType);
ilGenerator.Emit(OpCodes.Call,typeGetTypeFromHandleMethodInfo);
// value laden und zu string machen
ilGenerator.Emit(OpCodes.Ldarg_2);
ilGenerator.Emit(OpCodes.Callvirt,objectToStringMethodInfo);
// {Enum.Parse(typeof(MemberType),value)}
ilGenerator.Emit(OpCodes.Call,enumParseMethodInfo);
ilGenerator.Emit(OpCodes.Unbox_Any,memberType);
}
if (propertyInfo!=null)
// Property setzen
ilGenerator.Emit(OpCodes.Callvirt,propertyInfo.GetSetMethod(true));
else
// Feld setzen
ilGenerator.Emit(OpCodes.Stfld,fieldInfo);
// fertig
ilGenerator.Emit(OpCodes.Ret);
}
}
}
/// <summary>
/// Gibt das MethodInfo der zum übergebenen Typ gehörenden Convert.ToXY-Method zurück (null, falls keine passende gefunden)
/// </summary>
/// <param name="targetType">Typ, in den konvertiert werden soll</param>
/// <returns>das MethodInfo der zum übergebenen Typ gehörenden Convert.ToXY-Method zurück (null, falls keine passende gefunden)</returns>
private static MethodInfo getConvertToMethodInfo(Type targetType)
{
// Signatur der gesuchen Methode ermitteln
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;
// Methode mit der entsprechenden Signatur suchen und zurückgeben
return typeof(Convert).GetMethod(methodName,new Type[]{typeof(object)});
}
}
Schlagwörter: DynamicFieldAccessor, Felder, Properties, Reflection, IL-Generator
letzte Änderung am Code: 27.03.2008