// // Author: James Nies // Contributor: Tobias Hertkorn // Date: 5/31/2005 // Description: The GenericPropertyAccessor class provides fast dynamic access // to a property of a specified target class. // // *** This code was written by James Nies and has been provided to you, *** // *** free of charge, for your use. I assume no responsibility for any *** // *** undesired events resulting from the use of this code or the *** // *** information that has been provided with it . *** // using System; using System.Reflection; using System.Reflection.Emit; using System.Threading; using System.Collections; namespace FastDynamicPropertyAccessor { /// /// The GenericPropertyAccessor class provides fast dynamic access /// to a property of a specified target class. /// public class GenericPropertyAccessor : IGenericPropertyAccessor { /// /// Creates a new property accessor. /// /// Property name. public GenericPropertyAccessor(string property) { this.mTargetType = typeof(T); this.mProperty = property; PropertyInfo propertyInfo = this.mTargetType.GetProperty(property); // // Make sure the property exists // if (propertyInfo == null) { throw new PropertyAccessorException( string.Format("Property \"{0}\" does not exist for type " + "{1}.", property, this.mTargetType)); } else { this.mCanRead = propertyInfo.CanRead; this.mCanWrite = propertyInfo.CanWrite; this.mPropertyType = propertyInfo.PropertyType; } } /// /// Gets the property value from the specified target. /// /// Target object. /// Property value. public V Get(T target) { if (mCanRead) { if (this.mEmittedPropertyAccessor == null) { this.Init(); } return this.mEmittedPropertyAccessor.Get(target); } else { throw new PropertyAccessorException( string.Format("Property \"{0}\" does not have a get method.", mProperty)); } } /// /// Sets the property for the specified target. /// /// Target object. /// Value to set. public void Set(T target, V value) { if (mCanWrite) { if (this.mEmittedPropertyAccessor == null) { this.Init(); } // // Set the property value // this.mEmittedPropertyAccessor.Set(target, value); } else { throw new PropertyAccessorException( string.Format("Property \"{0}\" does not have a set method.", mProperty)); } } /// /// Whether or not the Property supports read access. /// public bool CanRead { get { return this.mCanRead; } } /// /// Whether or not the Property supports write access. /// public bool CanWrite { get { return this.mCanWrite; } } /// /// The Type of object this property accessor was /// created for. /// public Type TargetType { get { return this.mTargetType; } } /// /// The Type of the Property being accessed. /// public Type PropertyType { get { return this.mPropertyType; } } private static Hashtable mTypeHash; private Type mTargetType; private string mProperty; private Type mPropertyType; private IGenericPropertyAccessor mEmittedPropertyAccessor; private bool mCanRead; private bool mCanWrite; /// /// This method generates creates a new assembly containing /// the Type that will provide dynamic access. /// private void Init() { // Create the assembly and an instance of the // property accessor class. Assembly assembly = EmitAssembly(); mEmittedPropertyAccessor = assembly.CreateInstance("Property") as IGenericPropertyAccessor; if (mEmittedPropertyAccessor == null) { throw new Exception("Unable to create property accessor."); } } /// /// Thanks to Ben Ratzlaff for this snippet of code /// http://www.codeproject.com/cs/miscctrl/CustomPropGrid.asp /// /// "Initialize a private hashtable with type-opCode pairs /// so i dont have to write a long if/else statement when outputting msil" /// static GenericPropertyAccessor() { mTypeHash = new Hashtable(); mTypeHash[typeof(sbyte)] = OpCodes.Ldind_I1; mTypeHash[typeof(byte)] = OpCodes.Ldind_U1; mTypeHash[typeof(char)] = OpCodes.Ldind_U2; mTypeHash[typeof(short)] = OpCodes.Ldind_I2; mTypeHash[typeof(ushort)] = OpCodes.Ldind_U2; mTypeHash[typeof(int)] = OpCodes.Ldind_I4; mTypeHash[typeof(uint)] = OpCodes.Ldind_U4; mTypeHash[typeof(long)] = OpCodes.Ldind_I8; mTypeHash[typeof(ulong)] = OpCodes.Ldind_I8; mTypeHash[typeof(bool)] = OpCodes.Ldind_I1; mTypeHash[typeof(double)] = OpCodes.Ldind_R8; mTypeHash[typeof(float)] = OpCodes.Ldind_R4; } /// /// Create an assembly that will provide the get and set methods. /// private Assembly EmitAssembly() { // // Create an assembly name // AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = "GenericPropertyAccessorAssembly"; // // Create a new assembly with one module // AssemblyBuilder newAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); ModuleBuilder newModule = newAssembly.DefineDynamicModule("Module"); // // Define a public class named "Property" in the assembly. // TypeBuilder myType = newModule.DefineType("Property", TypeAttributes.Public | TypeAttributes.Sealed); // // Mark the class as implementing IPropertyAccessor. // myType.AddInterfaceImplementation(typeof(IGenericPropertyAccessor)); // Add a constructor ConstructorBuilder constructor = myType.DefineDefaultConstructor(MethodAttributes.Public); // // Define a method for the get operation. // Type[] getParamTypes = new Type[] { typeof(T) }; Type getReturnType = typeof(V); MethodBuilder getMethod = myType.DefineMethod("Get", MethodAttributes.Public | MethodAttributes.Virtual, getReturnType, getParamTypes); // // From the method, get an ILGenerator. This is used to // emit the IL that we want. // ILGenerator getIL = getMethod.GetILGenerator(); // // Emit the IL. // MethodInfo targetGetMethod = this.mTargetType.GetMethod("get_" + this.mProperty); if (targetGetMethod != null) { getIL.DeclareLocal(typeof(V)); getIL.Emit(OpCodes.Ldarg_1); //Load the first argument //(target object) getIL.EmitCall(OpCodes.Call, targetGetMethod, null); //Get the property value getIL.Emit(OpCodes.Stloc_0); //Store it getIL.Emit(OpCodes.Ldloc_0); } else { getIL.ThrowException(typeof(MissingMethodException)); } getIL.Emit(OpCodes.Ret); // // Define a method for the set operation. // Type[] setParamTypes = new Type[] { typeof(T), typeof(V) }; Type setReturnType = null; MethodBuilder setMethod = myType.DefineMethod("Set", MethodAttributes.Public | MethodAttributes.Virtual, setReturnType, setParamTypes); // // From the method, get an ILGenerator. This is used to // emit the IL that we want. // ILGenerator setIL = setMethod.GetILGenerator(); // // Emit the IL. // MethodInfo targetSetMethod = this.mTargetType.GetMethod("set_" + this.mProperty); if (targetSetMethod != null) { Type paramType = targetSetMethod.GetParameters()[0].ParameterType; setIL.DeclareLocal(paramType); setIL.Emit(OpCodes.Ldarg_1); //Load the first argument //(target object) setIL.Emit(OpCodes.Ldarg_2); //Load the second argument //(value object) setIL.EmitCall(OpCodes.Callvirt, targetSetMethod, null); //Set the property value } else { setIL.ThrowException(typeof(MissingMethodException)); } setIL.Emit(OpCodes.Ret); // // Load the type // myType.CreateType(); return newAssembly; } } }