// // Author: James Nies (23/05/2005) // Contributor: Tobias Hertkorn (31/05/2006) - Generics support. // Contributor: Omer van Kloeten (21/06/2006) - CodeDom instead of pure IL. // 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; using System.CodeDom; using System.CodeDom.Compiler; using Microsoft.CSharp; 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 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(); this.mEmittedPropertyAccessor = assembly.CreateInstance("FastDynamicPropertyAccessor.Property") as IGenericPropertyAccessor; if (this.mEmittedPropertyAccessor == null) { throw new Exception("Unable to create property accessor."); } } /// /// Create an assembly that will provide the get and set methods. /// private Assembly EmitAssembly() { PropertyInfo pi = typeof(T).GetProperty(this.mProperty); bool hasGet = (pi.GetGetMethod() != null), hasSet = (pi.GetSetMethod() != null); // Create the type CodeCompileUnit compileUnit = new CodeCompileUnit(); CodeNamespace ns = new CodeNamespace("FastDynamicPropertyAccessor"); CodeTypeDeclaration propertyAccessor = new CodeTypeDeclaration("Property"); propertyAccessor.Attributes |= MemberAttributes.Final | MemberAttributes.Public; propertyAccessor.BaseTypes.Add(typeof(IGenericPropertyAccessor)); // // Generate Get method // CodeMemberMethod get = new CodeMemberMethod(); get.Name = "Get"; get.ReturnType = new CodeTypeReference(typeof(V)); get.Parameters.Add(new CodeParameterDeclarationExpression(typeof(T), "target")); get.Attributes &= ~MemberAttributes.AccessMask; get.Attributes |= MemberAttributes.Public; if (hasGet) { get.Statements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("target"), this.mProperty))); } else { get.Statements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(MissingMethodException)))); } propertyAccessor.Members.Add(get); // // Generate Set method // CodeMemberMethod set = new CodeMemberMethod(); set.Name = "Set"; set.Parameters.Add(new CodeParameterDeclarationExpression(typeof(T), "target")); set.Parameters.Add(new CodeParameterDeclarationExpression(typeof(V), "value")); set.Attributes &= ~MemberAttributes.AccessMask; set.Attributes |= MemberAttributes.Public; if (hasSet) { set.Statements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("target"), this.mProperty), new CodeVariableReferenceExpression("value"))); } else { set.Statements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(MissingMethodException)))); } propertyAccessor.Members.Add(set); ns.Types.Add(propertyAccessor); compileUnit.Namespaces.Add(ns); // Reference assemblies (for the original type, return value and the IGenericPropertyAccessor) CompilerParameters parameters = new CompilerParameters(); parameters.ReferencedAssemblies.Add(typeof(IGenericPropertyAccessor).Assembly.Location); parameters.ReferencedAssemblies.Add(typeof(T).Assembly.Location); parameters.ReferencedAssemblies.Add(typeof(V).Assembly.Location); // Generation in memory means that no temporary file will be created. parameters.GenerateInMemory = true; // Compile the assembly CompilerResults cr = CSharpCodeProvider.CreateProvider("C#").CompileAssemblyFromDom(parameters, compileUnit); if (cr.Errors.Count > 0) { throw new PropertyAccessorException("Errors have been encountered while creating the dynamic assembly."); } return cr.CompiledAssembly; } } }