|

Introduction
Reflection is very useful for dynamic processing. However, if you have to repeatedly reflect on a property, say in a processing loop, you'll soon find that it can lead to performance problems. I ran into this particular problem in developing a rules engine that will be able to validate collections. I thought I would share this snippet of code, since I think it could be used in a variety of situations.
In this article I'll provide a fast, alternative solution for dynamic property access.
Implementation
My goal was to develop a class that could create a Type at runtime for direct access to the Get and Set methods of a property. This class would be provided with the target object Type and the property name it should access. I had considered runtime compiling, but then I learned about Reflection.Emit and its ability to create types at runtime through the use of MSIL. This was my first experience writing MSIL code and I found Ben Ratzlaff's Populating a PropertyGrid using Reflection.Emit to be a very helpful start.
In order to be able to compile code against against a Type that will be generated at runtime, an interface had to be created to define the generated types.
public interface IPropertyAccessor
{
object Get(object target);
void Set(object target, object value);
}
The concrete PropertyAccessor class generates a Type at runtime that conforms to this interface and serves as a proxy layer to the generated Type. In its constructor it simply needs to be provided with the target object Type and the name of the property it should provide access to. All of the Reflection.Emit code is performed in the EmitAssembly method.
public class PropertyAccessor : IPropertyAccessor
{
public PropertyAccessor(Type targetType, string property)
{
this.mTargetType = targetType;
this.mProperty = property;
PropertyInfo propertyInfo =
targetType.GetProperty(property);
if(propertyInfo == null)
{
throw new
PropertyAccessorException(string.Format("Property \"{0}\" does" +
" not exist for type " + "{1}.", property, targetType));
}
else
{
this.mCanRead = propertyInfo.CanRead;
this.mCanWrite = propertyInfo.CanWrite;
this.mPropertyType = propertyInfo.PropertyType;
}
}
public object Get(object 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));
}
}
public void Set(object target, object value)
{
if(mCanWrite)
{
if(this.mEmittedPropertyAccessor == null)
{
this.Init();
}
this.mEmittedPropertyAccessor.Set(target, value);
}
else
{
throw new
PropertyAccessorException(string.Format("Property \"{0}\" does" +
" not have a set method.", mProperty));
}
}
public bool CanRead
{
get
{
return this.mCanRead;
}
}
public bool CanWrite
{
get
{
return this.mCanWrite;
}
}
public Type TargetType
{
get
{
return this.mTargetType;
}
}
public Type PropertyType
{
get
{
return this.mPropertyType;
}
}
private Type mTargetType;
private string mProperty;
private Type mPropertyType;
private IPropertyAccessor mEmittedPropertyAccessor;
private Hashtable mTypeHash;
private bool mCanRead;
private bool mCanWrite;
private void Init()
{
this.InitTypes();
Assembly assembly = EmitAssembly();
mEmittedPropertyAccessor =
assembly.CreateInstance("Property") as IPropertyAccessor;
if(mEmittedPropertyAccessor == null)
{
throw new Exception("Unable to create property accessor.");
}
}
private void InitTypes()
{
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;
}
private Assembly EmitAssembly()
{
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "PropertyAccessorAssembly";
AssemblyBuilder newAssembly =
Thread.GetDomain().DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.Run);
ModuleBuilder newModule =
newAssembly.DefineDynamicModule("Module");
TypeBuilder myType =
newModule.DefineType("Property", TypeAttributes.Public);
myType.AddInterfaceImplementation(typeof(IPropertyAccessor));
ConstructorBuilder constructor =
myType.DefineDefaultConstructor(MethodAttributes.Public);
Type[] getParamTypes = new Type[] {typeof(object)};
Type getReturnType = typeof(object);
MethodBuilder getMethod =
myType.DefineMethod("Get",
MethodAttributes.Public | MethodAttributes.Virtual,
getReturnType,
getParamTypes);
ILGenerator getIL = getMethod.GetILGenerator();
MethodInfo targetGetMethod = this.mTargetType.GetMethod("get_" +
this.mProperty);
if(targetGetMethod != null)
{
getIL.DeclareLocal(typeof(object));
getIL.Emit(OpCodes.Ldarg_1);
getIL.Emit(OpCodes.Castclass, this.mTargetType);
getIL.EmitCall(OpCodes.Call, targetGetMethod, null);
if(targetGetMethod.ReturnType.IsValueType)
{
getIL.Emit(OpCodes.Box, targetGetMethod.ReturnType);
}
getIL.Emit(OpCodes.Stloc_0);
getIL.Emit(OpCodes.Ldloc_0);
}
else
{
getIL.ThrowException(typeof(MissingMethodException));
}
getIL.Emit(OpCodes.Ret);
Type[] setParamTypes = new Type[] {typeof(object), typeof(object)};
Type setReturnType = null;
MethodBuilder setMethod =
myType.DefineMethod("Set",
MethodAttributes.Public | MethodAttributes.Virtual,
setReturnType,
setParamTypes);
ILGenerator setIL = setMethod.GetILGenerator();
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);
setIL.Emit(OpCodes.Castclass, this.mTargetType);
setIL.Emit(OpCodes.Ldarg_2);
if(paramType.IsValueType)
{
setIL.Emit(OpCodes.Unbox, paramType);
if(mTypeHash[paramType]!=null)
{
OpCode load = (OpCode)mTypeHash[paramType];
setIL.Emit(load);
}
else
{
setIL.Emit(OpCodes.Ldobj,paramType);
}
}
else
{
setIL.Emit(OpCodes.Castclass, paramType);
}
setIL.EmitCall(OpCodes.Callvirt,
targetSetMethod, null);
}
else
{
setIL.ThrowException(typeof(MissingMethodException));
}
setIL.Emit(OpCodes.Ret);
myType.CreateType();
return newAssembly;
}
}
Discussion
Although the PropertyAccessor class must reflect on the target Type the first time the property is accessed (for either read or write), this reflection only has to be done once. All subsequent calls to Get or Set will use the generated IL code.
Run the sample project for a performance demonstration. I've also included an NUnit test fixture.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 42 (Total in Forum: 42) (Refresh) | FirstPrevNext |
|
 |
|
|
First of all, thanks a lot for this piece of work - It is very helpful in what I am trying to do - Pretty much exactly what i was looking for for a long time! I also extended it with a PropertyAccessorMgr class which auto-manages all property accessors that have been created and only creates a new one if it does not already exist.
Now is there an easy way to easily invoke methods or ctors dynamically? I'm not sure if this is the right place to ask but I couldn't seem to find any more info on this matter - And I figured if dynamic property invocation is working, why not methods or ctors? I'm confident someone here must have the answer 
Edit: I realized 2 things that should be mentioned: 1. The containing class must be public (or -I assume- somehow accessible to the PropertyAccessor). 2. You MUST refer to the actual class of the object whose property you want to evaluate. You cannot use the PropertyAccessor of an underlying type or interface.
modified on Tuesday, August 5, 2008 5:08 PM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Quick variation based on lambda functions.
>---------------------------------------- class LambdaPropertyAccessor<T> { private Dictionary<string, Func<T, object>> propAccessors = new Dictionary<string, Func<T, object>> ();
public LambdaPropertyAccessor() { Type type = typeof (T);
foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance)) { if (!pi.CanRead) continue;
ParameterExpression parameter = Expression.Parameter(type, "r");
Expression<Func<T, object>> lambda; if (pi.PropertyType.IsValueType) { lambda = Expression.Lambda<Func<T, object>> ( Expression.TypeAs(Expression.Property(parameter, pi.Name), typeof(object)), parameter); } else { lambda = Expression.Lambda<Func<T, object>> (Expression.Property(parameter, pi.Name), parameter); }
propAccessors.Add(pi.Name, lambda.Compile()); } }
public object Get(T obj, string propName) { return propAccessors[propName](obj); } } <----------------------------------------
1000000 property gets on integer... Direct access ms: 15,625 PropertyAccessor (Reflection.Emit) ms: 78,125 Reflection ms: 4687,5 Lambda ms: 218,75
1000000 property gets on strings... Direct access ms: 15,625 PropertyAccessor (Reflection.Emit) ms: 46,875 Reflection ms: 4765,625 Lambda ms: 203,125
|
| Sign In·View Thread·PermaLink | 3.67/5 (2 votes) |
|
|
|
 |
|
|
 |
|
|
Property setter implementation will be a "statement lambda". I'm not sure that it can be implemented using System.Linq.Expressions.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I am creating new PropertyAccessor objects in a loop at the rate of about 100 per minute or so. My pagefile grows at a pretty brisk pace until it overwhelms my computer. This is in conjunction with table adapters for a data processing application. when I comment out (Property Adapter) pa.Set(...) in all 18 places, this no longer happens. I guess its the assemblies are being cached there. How do I remove them?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
If you're not already doing so, I would recommend caching PropertyAccessors so that you never create more than one instance for a given property of a Type. Actually, without doing this, the PropertyAccessor class would be slower than reflection. If caching the PropertyAccessor is not feasible for your particular application, you can unload the cached property assemblies by creating the PropertyAccessors and performing your operations within a new AppDomain. When done with the processing, you can then unload that app domain and the assemblies created under it. However, this introduces code complexity and you'd have to use Remoting to communicate across the AppDomain boundaries.
|
| Sign In·View Thread·PermaLink | 4.00/5 (1 vote) |
|
|
|
 |
|
|
Hi, I have been using this code snippet & it worked perfect in .net 1.0 & 1.1. But ever since I ported to 2.0 there is a strange problem:
AssemblyBuilder newAssembly = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); ModuleBuilder newModule = newAssembly.DefineDynamicModule("Module"); TypeBuilder myType = newModule.DefineType("Property", TypeAttributes.Public);
It throws exception in DefineType. Say this code is executed 200 time. It will run fine 199 times but once it will throw exception. But it will surely throw exception everytime I run. And it throws exception only when I debug using VS2005 not when I directly run.
Does anyone have any clue to this problem.
thanks, Mandeep.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi there!
First of all - wow, James, that's excellent work! I have never used Emit before - and here it really makes so much sense!
I am using VS 2005 - so I thought, I could do a quick port to .NET 2.0, just to learn something about Emit. Turns out, it is tragically easy to implement it using generics. That is, implementing it using your great code. Seriously, it boils down to removing the casting stuff in your code.
But those easy modification give you an additional speed boost and actually cuts the get and set on value types in half! And there is still a significant speed gain on regular objects as well.
Have fun downloading GenericPropertyAccessor from my blog, plus an updated test app.
For those too lazy to click on the link in my blog - here is the direct link: GenericPropertyAccessor as Zip
Have fun, Tobi
P.S.: I have no idea, what to put in the header of these files?! How do I "clearly mark" that I changed the code but hugely depended on James' work? Is what I did ok, or should I change the header? Help!
--- Tobi + C# = T#
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
First, I thank James very much for the great work!
Hi Tobi, the Generic implemation is very nice. I couldn't pass the property types dynamically to the GenericPropertyAccessor. Eg: The below code works fine for string type property.
GenericPropertyAccessor propertyAccessor = new GenericPropertyAccessorstring>("String");
but, I am using the above code in the iteration of a collection to display all the property values, so I should pass the property type (higleted) dynamically. any help?
LK.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hey Leela,
I guess for your kind of situation you would have to use James' original PropertyAccessor. But actually I am not too sure if I understand the situation you are trying to solve.
Sorry.  Tobi
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi, great work, I've been using it and it works fine, but I have a problem: did you ever try accessing properties from a value type? If you try, for example, to get the property "Width" from a System.Drawing.Size, you don't get the value, but a "strange" number that seems to be a memory address... I'm totally ignorant about Emit and I'm not able to see where the problem is, did you verify the same problems and, if you did, do you have some hints about it? Thanks, bye, 
Wasp
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hi, this code will be really helpful to me but i also need getting name and value of all public properties. and i also don't have enough knowledge about IL coding. i'll give Type and BindingFlags as parameters instead of property name, and it will give me property info collection. thanks for your help..
-- savas --
|
| Sign In·View Thread·PermaLink | 2.00/5 (2 votes) |
|
|
|
 |
|
|
Given the code as is and the reflection API (you already know about BindingFlags) this should be a piece of cake. Unless you need help coding arrays/collections...?
|
| Sign In·View Thread·PermaLink | 2.00/5 (2 votes) |
|
|
|
 |
|
|
How to make better (example of reflection) this situation on C#
// assembly1 class c1 { public void method1() {}
public int method2() { return 100; } }
// assembly2 class c2 { c1 _c1 = new c1(); c3 _c3 = new _c3(_c1); }
// assembly3 class c3(object _o) { _o.method1(); int y = 100; y = y + _o.method2(); // 200 MessageBox.Show(y.ToString()); // 200 }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
This is nice stuff, but I have one problem. When I try to set a null value I get a NullReferenceException when the type of the property is a ValueType. Is there a way to change the generated IL for the SetValue method so that it perfoms a null-check first and if so, set the value to the initial value of a ValueType (for example, 0 for ints)? I tried fixing it myself but the IL language is very difficult.
Thanks.
Pat
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Rather than put this logic in the IL of the emitted property accessor, you could place it in the Set method of the concrete PropertyAccessor (see code below). It might even make sense to make the PropertyAccessor a base class with a protected abstract DefaultValue property. You could create a concrete implementation for each value type, but then you would also need a factory to create the appropriate property accessor...
public void Set(object target, object value) { if(mCanWrite) { if(this.mEmittedPropertyAccessor == null) { this.Init(); }
// // If the value to assign is null and this property // accessor is for a value type, use a default value // instead // object newValue = value; if(newValue == null && PropertyType.IsValueType) { newValue = DefaultValue; }
// // Set the property value // this.mEmittedPropertyAccessor.Set(target, newValue); } else { throw new PropertyAccessorException(string.Format("Property \"{0}\" does" + " not have a set method.", mProperty)); } }
James
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
James,
nice work. It was *almost* what i needed. I had the same use-case regarding public fields instead of properties. So I sat down and refactored a bit cheers, it refactored just fine (which compliments your style).
I factored out an (abstract) base class MemberAccessor, which has two descendants, FieldAccessor and PropertyAccessor, obviously.
The descendants sole task is to implement EmitSetter() and EmitGetter() (which are abstract methods on the baseclass.
I even sat down and refactored the tests. All tests relevant to both fields and methods have moved into a baseclass MemberAccessorTest, and the derived TestFixtures PropertyAccessorTest/FieldAccesorTest extend this with specific tests (e.g. there is no such thing as a WriteOnly field, and no such thing as a 'const' property).
Obviously there has been major find/replace Property->Member to keep the naming consistent with the purposes (e.g. IMemberAccessor instead of IPropertyAccessor).
The icing on the cake may be a factory method MemberAccessor.Make(...) which will instantiate either a PropertyAccessor or FieldAccessor by reflection.
If you are interested i will send/upload (?) the extended project 'FastDynamicMemberAccessor.proj'.
PS1. performance might have gone up by a chance to EmitAssembly that creates a 'sealed' class (gives compiler a huge optimization clue) PS2. yes I use public fields for simple XmlSerializer-able classes  PS3. Needless to say, SampleApp runs without modification PS4. The sharing of 36 test cases across the field/property variants has been achieved by implementing PropertyAccessorTestObject/FieldAccessorTestObject with a shared interface IMemberAccessorTestObject that interface is *only* used for test asserts, not for member access, of course
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
| | |