简体   繁体   中英

how to call a method in setter through reflection C#

I am dynamically creating a class which inherits ViewModelBase of MvvmLight. In the class the properties should implement that INotifyPropertyChanged interface method RaiseEvent.

This is the complete code

// Our intermediate language generator
ILGenerator ilgen;

// The assembly builder
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("PeopleLibrary"), AssemblyBuilderAccess.RunAndSave);

// The module builder
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("PeopleLibrary", "PeopleLibrary.dll");

// The person class builder
TypeBuilder personBuilder = modBuilder.DefineType("PeopleLibrary.Person", TypeAttributes.Class | TypeAttributes.Public, typeof(ViewModelBase));

// The default constructor
ConstructorBuilder ctorBuilder = personBuilder.DefineDefaultConstructor(MethodAttributes.Public);

// Two fields: m_firstname, m_lastname
FieldBuilder fBuilderFirstName = personBuilder.DefineField("firstname", typeof(string), FieldAttributes.Private);
FieldBuilder fBuilderLastName = personBuilder.DefineField("lastname", typeof(string), FieldAttributes.Private);

// Two properties for this object: FirstName, LastName
PropertyBuilder pBuilderFirstName = personBuilder.DefineProperty("FirstName", System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);
PropertyBuilder pBuilderLastName = personBuilder.DefineProperty("LastName", System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);

// Custom attributes for get, set accessors
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName;

// get,set accessors for FirstName
MethodBuilder mGetFirstNameBuilder = personBuilder.DefineMethod("get_FirstName", getSetAttr, typeof(string), Type.EmptyTypes);

// Code generation
ilgen = mGetFirstNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, fBuilderFirstName); // returning the firstname field
ilgen.Emit(OpCodes.Ret);

MethodBuilder mSetFirstNameBuilder = personBuilder.DefineMethod("set_FirstName", getSetAttr, null, new Type[] { typeof(string) });

// Code generation
ilgen = mSetFirstNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Stfld, fBuilderFirstName); // setting the firstname field from the first argument (1)
ilgen.Emit(OpCodes.Ret);

// get,set accessors for LastName
MethodBuilder mGetLastNameBuilder = personBuilder.DefineMethod("get_LastName", getSetAttr, typeof(string), Type.EmptyTypes);

// Code generation
ilgen = mGetLastNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, fBuilderLastName); // returning the firstname field
ilgen.Emit(OpCodes.Ret);

MethodBuilder mSetLastNameBuilder = personBuilder.DefineMethod("set_LastName", getSetAttr, null, new Type[] { typeof(string) });

// Code generation
ilgen = mSetLastNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Stfld, fBuilderLastName); // setting the firstname field from the first argument (1)
ilgen.Emit(OpCodes.Ret);

// Assigning get/set accessors
pBuilderFirstName.SetGetMethod(mGetFirstNameBuilder);
pBuilderFirstName.SetSetMethod(mSetFirstNameBuilder);

pBuilderLastName.SetGetMethod(mGetLastNameBuilder);
pBuilderLastName.SetSetMethod(mSetLastNameBuilder);

// Now, a custom method named GetFullName that concatenates FirstName and LastName properties
MethodBuilder mGetFullNameBuilder = personBuilder.DefineMethod("GetFullName", MethodAttributes.Public, typeof(string), Type.EmptyTypes);

// Code generation
ilgen = mGetFullNameBuilder.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, mGetFirstNameBuilder); // getting the firstname
ilgen.Emit(OpCodes.Ldstr, " "); // an space
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, mGetLastNameBuilder); // getting the lastname

// We need the 'Concat' method from string type
MethodInfo concatMethod = typeof(String).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });

ilgen.Emit(OpCodes.Call, concatMethod); // calling concat and returning the result
ilgen.Emit(OpCodes.Ret);

// Another constructor that initializes firstname and lastname
ConstructorBuilder ctorBuilder2 = personBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(string), typeof(string) });
ctorBuilder2.DefineParameter(1, ParameterAttributes.None, "firstname");
ctorBuilder2.DefineParameter(2, ParameterAttributes.None, "lastname");

// Code generation
ilgen = ctorBuilder2.GetILGenerator();

// First of all, we need to call the base constructor,
// the Object's constructor in this sample
Type objType = Type.GetType("System.Object");
ConstructorInfo objCtor = objType.GetConstructor(Type.EmptyTypes);

ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, objCtor); // calling the Object's constructor

ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_1);
ilgen.Emit(OpCodes.Call, mSetFirstNameBuilder); // setting the firstname field from the first argument (1)
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldarg_2);
ilgen.Emit(OpCodes.Call, mSetLastNameBuilder);  // setting the lastname field from the second argument (2)
ilgen.Emit(OpCodes.Ret);

// Finally, create the type and save the assembly
var type = personBuilder.CreateType();

asmBuilder.Save("PeopleLibrary.dll");

In that while creating properties in setter i need to do this

public string StudentName
{
    get
    {
        return this.studentName;
    }

    set
    {
        this.Set(() => this.StudentName, ref this.studentName, value);
    }
}

But now as per the code sample i found its coming like this in reflection.

private string firstname;

public string FirstName
{
  get
  {
    return this.firstname;
  }
  set
  {
    this.firstname = obj0;
  }
}

My requirement is i need to call the base class method Set in the setter as i shown. I modified the code and inherited the base class in reflection. But i am not able to this setter stuff.

Please help me. Thanks in advance !

Here is a little trick for such problems. Download this add-in for Reflector.

I've just compiled the following little sample:

    private string studentName;
    public string StudentName
    {
        get { return this.studentName; }
        set { this.Set(() => this.StudentName, ref this.studentName, value); }
    }

    private void Set(Func<string> func, ref string s, string value)
    {           
    }

And viewing the property setter in Reflector with the Reflection.Emit language:

public MethodBuilder BuildMethodset_StudentName(TypeBuilder type)
{
    // Declaring method builder
    MethodBuilder method = type.DefineMethod("set_StudentName");
    // Preparing Reflection instances
    MethodInfo method1 = typeof(Program).GetMethod(
        "<set_StudentName>b__0", 
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 
        null, 
        new Type[]{
            }, 
        null
        );
    ConstructorInfo ctor2 = typeof(System.Func<>).MakeGenericType(typeof(String)).GetConstructor(
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 
        null, 
        new Type[]{
            typeof(Object),
            typeof(IntPtr)
            }, 
        null
        );
    FieldInfo field3 = typeof(Program).GetField("studentName", BindingFlags.Public | BindingFlags.NonPublic);
    MethodInfo method4 = typeof(Program).GetMethod(
        "Set", 
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, 
        null, 
        new Type[]{
            typeof(System.Func<>).MakeGenericType(typeof(String)),
            typeof(String&),
            typeof(String)
            }, 
        null
        );
    // Method attributes
    method.Attributes = 
          System.Reflection.MethodAttributes.Public
        | System.Reflection.MethodAttributes.HideBySig;
    // Setting return type
    method.SetReturnType(typeof(Void));
    // Adding parameters
    method.SetParameters(
        typeof(String)
        );
    // Parameter value
    ParameterBuilder value =  method.DefineParameter(1, ParameterAttributes.None, "value");
    ILGenerator gen =  method.GetILGenerator();
    // Writing body
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldftn,method1);
    gen.Emit(OpCodes.Newobj,ctor2);
    gen.Emit(OpCodes.Ldarg_0);
    gen.Emit(OpCodes.Ldflda,field3);
    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Call,method4);
    gen.Emit(OpCodes.Nop);
    gen.Emit(OpCodes.Ret);
    // finished
    return method;
}

The <set_StudentName>b__0 method is the generated one for the lambda in the setter, which looks like this:

[CompilerGenerated]
private string <set_StudentName>b__0()
{
    return this.StudentName;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM