C# Getting PropertyInfo within setter using PropertyBuilder

490 views Asked by At

I'm attempting to write some .NET code that dynamically generates various types from a list of simple interfaces containing only property setters/getters. One of the types I would like to generate from the list is one that supports the given interfaces (with auto-generated backing fields) but that also tracks all property set operations, recording which properties got changed (and in the future possibly the time, context, user, etc. that made the change). After building samples in C# and examining them with ILDASM, I've got it working, but there is one part of the code for which the IL seems much more complicated than it should be. For this type, inside the code that defines the setter for a property, I set the value into the field (simple enough), but then I need to get the PropertyInfo for the property I'm setting (so later on a user can enumerate the PropertyInfos for the properties that were changed). In order to get the PropertyInfo for the property whose setter I'm in the middle of defining, I've got the following code (middle section):

...
PropertyBuilder pb = tb.DefineProperty(property.Name, property.Attributes, property.PropertyType, null);
MethodAttributes attr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
MethodBuilder setmb = tb.DefineMethod(SetPropertyMethodPrefix + property.Name, attr, null, new Type[] { property.PropertyType });
ILGenerator setgen = setmb.GetILGenerator();
...
// store the value in the backing field
setgen.Emit(OpCodes.Ldarg_0);
setgen.Emit(OpCodes.Ldarg_1);
setgen.Emit(OpCodes.Stfld, fb);
// get ready to give the change tracker the PropertyInfo for this property
setgen.Emit(OpCodes.Ldarg_0);
setgen.Emit(OpCodes.Ldfld, changeTrackerField);


// get the PropertyInfo for this property (there has to be a better way!)
setgen.Emit(OpCodes.Ldarg_0);
setgen.Emit(OpCodes.Call, typeof(object).GetMethod("GetType"));
// alternatively, instead of the two lines above, I can get the type token directly and get the type from there, which may or may not be any faster...
// setgen.Emit(OpCodes.Ldtoken, tb.TypeToken.Token);
// setgen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) }));
setgen.Emit(OpCodes.Ldstr, property.Name);
setgen.Emit(OpCodes.Callvirt, typeof(Type).GetMethod("GetProperty", new Type[] { typeof(string) }));


// give the PropertyInfo to our change tracking object
setgen.Emit(OpCodes.Callvirt, typeof(ChangeTracker).GetMethod("MarkPropertyChanged"));
setgen.Emit(OpCodes.Ret);

The middle section is equivalent to the C# code: GetType().GetProperty(<propertyName>), but it seems like there should be some faster way to get the PropertyInfo for the property I'm in the midst of building. The GetTypeFromHandle version seems like it may be more efficient (typeof(T) vs. this.GetType()), but I'm guessing there is a way to bypass GetProperty altogether, perhaps using PropertyBuilder.PropertyToken.

Is there a way to get the PropertyInfo instance for the property I'm in the middle of building without resorting to inserting IL code that does self-reflection?

2

There are 2 answers

0
IS4 On

I understand one would think there is some way to refer to a property (or an event) via its token in the method's body, but that's not possible. The only viable opcode would be ldtoken, but that only accepts methods, fields or types. I see your concerns, but I'd advise first doing some benchmarks, to see if calling GetProperty is really the performance bottleneck of the code. However, you have several other options to refer to the property.

  • Traverse all properties and check their token (emitted as an integer in the CIL).

  • Use a dictionary as a map from the property token to the actual PropertyInfo. Populate the dictionary in the constructor, or lazily.

  • Remember the order in which you build the properties, and use it to index the array returned by GetProperties. I suppose the order should be the same, but better check it.

0
hoodaticus On

To solve similar problems in the past I have created a statically-accessible Dictionary with the long being a simple, atomically-incremented unique ID. You can then retrieve the PropertyInfo - or any other object - using a static method call with the token you have created being hardcoded into the dynamic method.