如何通过反射在setter中调用方法

本文关键字:调用 方法 setter 何通过 反射 | 更新日期: 2023-09-27 18:18:36

我正在动态创建一个继承MvvmLight的ViewModelBase的类。在类中属性应该实现INotifyPropertyChanged接口方法RaiseEvent。

这是完整的代码
// 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");

在setter中创建属性时我需要这样做

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

但是现在,根据代码样本,我发现它在反射中是这样的。

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

我的要求是我需要调用setter中的基类方法Set,如所示。我修改了代码并在反射中继承了基类。但是我不能设置setter。

请帮帮我。

如何通过反射在setter中调用方法

对于此类问题有一个小技巧。下载这个插件

我刚刚编译了下面这个小示例:
    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)
    {           
    }

Reflection.Emit语言查看Reflector中的属性设置器:

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;
}

<set_StudentName>b__0方法是为setter中的lambda生成的方法,看起来像这样:

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