views:

61

answers:

2

Hello, 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(ObservableCollection<>).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.

+4  A: 

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);
Quartermeister
Thank you very much. This helped.
Maxmyd
+3  A: 

As per documentation to Ldfld, stack transition is the following

  1. An object reference (or pointer) is pushed onto the stack.
  2. The object reference (or pointer) is popped from the stack; the value of the specified field in the object is found.
  3. 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.

desco