Problem creating type with Reflection
I got a following base class:
public class ValidationItem
{
public ObservableCollection<object> GetFilteredValues( ObservableCollection<object> values)
{
return new ObservableCollection<object>(); // nothing here yet
}
}
I create a type which inherits this base type and I create a getter which is going to return a base class GetFilteredValues method result.
This is how a new property should look like:
public ObservableCollection<object> Values
{
get { return GetFilteredValues(_values); }
set { _values = value; }
}
This is what I do:
Type pType = typeof(ObservableCollection<object>);
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, pType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty( propertyName, PropertyAttributes.HasDefault, pType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
pType, Type.EmptyTypes);
getPropMthdBldr.SetReturnType(typeof(O开发者_JAVA百科bservableCollection<>).MakeGenericType(typeof(object)));
ILGenerator getIL = getPropMthdBldr.GetILGenerator();
MethodInfo minfo = typeof(ValidationItem).GetMethod("GetFilteredValues", new[] { typeof(ObservableCollection<object>) }); // it's not null so everything is ok here
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.EmitCall(OpCodes.Callvirt, minfo, Type.EmptyTypes);
getIL.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
But each time I run an app and use this created type, I get an error "Common Language Runtime detected an invalid program". What am I doing wrong?
Thanks in advance.
When you call GetFilteredValues
, the only thing on the stack is the ObservableCollection<object>
. Since GetFilteredValues
is an instance method, you also need to push this
. Add a second Ldarg_0
before the existing one so that you push it on the stack before _values
:
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.EmitCall(OpCodes.Callvirt, minfo, Type.EmptyTypes);
getIL.Emit(OpCodes.Ret);
As per documentation to Ldfld, stack transition is the following
- An object reference (or pointer) is pushed onto the stack.
- The object reference (or pointer) is popped from the stack; the value of the specified field in the object is found.
- The value stored in the field is pushed onto the stack.
So after executing
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
you will have only field reference on the evaluation stack (without 'this'). To fix, duplicate arg_0
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Dup);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.EmitCall(OpCodes.Callvirt, minfo, Type.EmptyTypes);
getIL.Emit(OpCodes.Ret);
This should help.
精彩评论