views:

154

answers:

1

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

+2  A: 

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.

Marc Gravell
+1 - too fast for me. In addition I want to point out that you probably don't want to add OpCodes.Nop statements and this could've been currSetIL.Emit(OpCodes.Call, typeof(string).GetMethod("IsNullOrEmpty")) - shorter, same thing?
Benjamin Podszun
@Benjamin: Yes, absolutely the "nop" is total overhead from the debugger and can be dropped.
Marc Gravell
OK, thanks for the fast answer, (and yes, the nop is overhead :-) but i took it in, to show a 1:1 convertion form MSIL to the EMIT, command)So in short is there a tool or something that will get me from MSIL to the EMIT sting. There are many other lines in the MSIL code that has to be convertet?Or better yet, can i add a method directly to the generateed type. Eg.g something like thisproperty.SetSetMethod(MyMethod); // This wont work because the SetMethod takes a Methodbuilder and not a method, so can i create one based on another method somehow? It will solve the problem.
ReLoad
Hi AllAfter some condidaration i decided to go another way around the problem. (Using dynamic) This is more transparent and easier. Thanks for the comments and replyes.Regards ReLoad
ReLoad