2
votes

Hej All

I have some code that builds a new TYPE runtime, it sets the GET and SET method using MethodBuilder. (Its an exampel from the web, and thanks to the Guy, that wrote it, i unfortantly lost the ref. to him, but he is in my thoughts)

TypeBuilder typeBuilder = module.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.Class);

I add a method to the class this way.

    MethodAttributes GetSetAttr =
      MethodAttributes.Public |
      MethodAttributes.HideBySig;

// Define the "get" accessor method for current private field.
                MethodBuilder currGetPropMthdBldr =
                    typeBuilder.DefineMethod("get_value",
                                               GetSetAttr,
                                               typeof(string),
                                               Type.EmptyTypes);

                // Intermediate Language stuff...
                ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator();
                currGetIL.Emit(OpCodes.Ldarg_0);
                currGetIL.Emit(OpCodes.Ldfld, field);
                currGetIL.Emit(OpCodes.Ret);

                // Define the "set" accessor method for current private field.
                MethodBuilder currSetPropMthdBldr =
                    typeBuilder.DefineMethod("set_value",
                                               GetSetAttr,
                                               null,
                                               new Type[] { typeof(string) });

                // Again some Intermediate Language stuff...
                ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
                currSetIL.Emit(OpCodes.Ldarg_0);
                currSetIL.Emit(OpCodes.Ldarg_1);
                currSetIL.Emit(OpCodes.Stfld, field);
                currSetIL.Emit(OpCodes.Ret);



                // Last, we must map the two methods created above to our PropertyBuilder to 
                // their corresponding behaviors, "get" and "set" respectively. 
                property.SetGetMethod(currGetPropMthdBldr);
                property.SetSetMethod(currSetPropMthdBldr);

It works fine, i would however like to change the setmethod to something more complex, so i write this test code.

public class Customer { private string _name; public string name { get { return _name; } set { if ( string.IsNullOrEmpty( value ) ) { throw new ValidationException("Please set a value"); } _name = value; } } public string lastname { get; set; } }

Compile and then use Reflector to get the MSIL.

.method public hidebysig specialname instance void set_name(string 'value') cil managed
{
    .maxstack 2
    .locals init (
        [0] bool CS$4$0000)
    L_0000: nop 
    L_0001: ldarg.1 
    L_0002: call bool [mscorlib]System.String::IsNullOrEmpty(string)
    L_0007: ldc.i4.0 
    L_0008: ceq 
    L_000a: stloc.0 
    L_000b: ldloc.0 
    L_000c: brtrue.s L_001a
    L_000e: nop 
    L_000f: ldstr "Please set a value"
    L_0014: newobj instance void [System.ComponentModel.DataAnnotations]System.ComponentModel.DataAnnotations.ValidationException::.ctor(string)
    L_0019: throw 
    L_001a: ldarg.0 
    L_001b: ldarg.1 
    L_001c: stfld string AnnotationTest.MainPage/Customer::_name
    L_0021: ret 
}

So the task is to implement it into the SET EMIT code.

                // Again some Intermediate Language stuff...
                ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator();
                currSetIL.Emit(OpCodes.Ldarg_0);
                currSetIL.Emit(OpCodes.Ldarg_1);
                currSetIL.Emit(OpCodes.Stfld, field);
                currSetIL.Emit(OpCodes.Ret);

This, is where i come short, i cant get it to work. It seams that i can's "just" copy the code over, and my MSIL skills are limited. hereunder is my error.

        currSetIL.Emit(OpCodes.Nop);  //       L_0000: nop 
        currSetIL.Emit(OpCodes.Ldarg_1);  //   L_0001: ldarg.1 
        currSetIL.Emit(OpCodes.Call bool [mscorlib]System.String::IsNullOrEmpty(string);// call bool [mscorlib]System.String::IsNullOrEmpty(string)

On the 3 line, these red underlines give thede errors...

  • Bool = ") Expetced"
  • [mscorlib]System = "; expected"
  • :: = ". expected"
  • and the last ) gives "invalid expression term string"

I wonder why i cant use reflector, the code should be ok? or?

The solution it to find a program / Method that displays MSIL code that can be used in the EMIT statement.

This is just an example so the code will change, so it's not a solution to answer the correct code (allthou it will be nice to get the example working) but a more permenent "way" of getting the correct MSIL from C#.

Peww, a long question, i hope i have everything in here.

Regards ReLoad

1
Re comment; AFAIK it'll be a fairly manual translation. Seriously, though, I think you can do all of this via Expression (especially in the case of new objects). Do you have an example of what you want to achieve? i.e. can you post the C# equivalent of what you want? - Marc Gravell♦

1 Answers

3
votes

currSetIL.Emit(OpCodes.Call bool [mscorlib]System.String::IsNullOrEmpty(string);// call bool [mscorlib]System.String::IsNullOrEmpty(string)

You need to get the MethodInfo - in this case:

currSetIL.EmitCall(OpCodes.Call,typeof(string).GetMethod("IsNullOrEmpty"),null);

Actually, though, I'd be looking into Expression for this - I believe there is a Compile for silverlight Expression, and you don't have to learn IL!

Note that if there are multiple overloads you generally have to do a lot more work to get the MethodInfo (it just happens that string.IsNullOrEmpty is easy to use). Also, note that "instance" methods should use OpCodes.Callvirt; static methods (like this one) should use OpCodes.Call.