C #, Как динамически создавать с помощью метода получения свойства Reflection.Emit в стиле mvvmlight - PullRequest
0 голосов
/ 25 октября 2019

Как создать свойство, как показано ниже, в Reflection.Emit

    private string _Name;

    public override string Name{ get => _Name; set => Set(ref _Name, value); }

Я пробовал это с Reflection.Emmit

    private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
    {
      // propertyName = "Name";
      // propertyType = typeof(string);
      // private string _Name;
      FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

      // public string Name {get; set;}
      PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
      // get;
      MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
      ILGenerator getIl = getPropMthdBldr.GetILGenerator();
      // get => _Name
      getIl.Emit(OpCodes.Ldarg_0);
      getIl.Emit(OpCodes.Ldfld, fieldBuilder);
      getIl.Emit(OpCodes.Ret);

      // set;
      MethodBuilder setPropMthdBldr =
          tb.DefineMethod("set_" + propertyName,
            MethodAttributes.Public |
            MethodAttributes.SpecialName |
            MethodAttributes.HideBySig,
            null, new[] { propertyType });

      ILGenerator setIl = setPropMthdBldr.GetILGenerator();


      var MvvmLightSetMethod = typeof(ObservableObject)
                               .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                               .Where(m => m.Name == "Set").ToList()[2];

      // set => Set(ref _Name, value);
      setIl.Emit(OpCodes.Mkrefany, fieldBuilder);
      setIl.Emit(OpCodes.Ldarg_0);
      setIl.Emit(OpCodes.Ldarg_1);
      setIl.Emit(OpCodes.Stfld, propertyName);
      setIl.Emit(OpCodes.Call, MvvmLightSetMethod);
      setIl.Emit(OpCodes.Ret);

      // public string Name {get; set;}
      propertyBuilder.SetGetMethod(getPropMthdBldr);
      propertyBuilder.SetSetMethod(setPropMthdBldr);
    }

Моя проблема

Если я запускаюэтот код и вызвать сеттер, я получаю «Недопустимое исключение токена класса» в set_Name (строка)

1 Ответ

0 голосов
/ 25 октября 2019

Ваш IL для вызова Set<T> абсолютно неверен.

Set<T> имеет следующую подпись:

protected bool Set<T>(
    ref T field,
    T newValue,
    [CallerMemberName] string propertyName = null)

Помните, что порядок методов вернулсяType.GetMethods не определено и может измениться. Не просто индексируйте это - убедитесь, что вы получите правильный метод, указав его сигнатуру. Это также универсальный метод, поэтому вам нужно вызвать MakeGenericMethod.

Я также не знаю, что вы пытаетесь сделать с Mkrefany. Похоже, что вы пытались сделать ref this.field, но это было сделано с Ldflda.

Наконец, Set<T> возвращает значение, которое вам нужно будет вытолкнуть, прежде чем вы сможете его вернуть.

Соберите все это вместе, и вы получите следующее:

var setMethod = (from method in typeof(ObservableObject).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                 where method.Name == "Set"
                 let parameters = method.GetParameters()
                 where parameters.Length == 3 &&
                 parameters[0].ParameterType.IsByRef &&
                 parameters[0].ParameterType.ContainsGenericParameters &&
                 parameters[1].ParameterType.ContainsGenericParameters &&
                 parameters[2].ParameterType == typeof(string)
                 select method).Single().MakeGenericMethod(propertyType);

// set => Set(ref _Name, value);
setIl.Emit(OpCodes.Ldarg_0); // Load 'this' -- we'll use it when calling Set later
// Stack: [this]
setIl.Emit(OpCodes.Ldarg_0); // Load 'this' again, for accessing 'this.field'
// Stack: [this, this]
setIl.Emit(OpCodes.Ldflda, fieldBuilder); // Load the address of 'this.field'
// Stack: [this, ref this.field]
setIl.Emit(OpCodes.Ldarg_1); // Load 'value'
// Stack: [this, ref this.field, value]
setIl.Emit(OpCodes.Ldstr, propertyName); // Load 'propertyName'
// Stack: [this, ref this.field, value, propertyName]
setIl.Emit(OpCodes.Call, setMethod);
// Stack: [bool]
setIl.Emit(OpCodes.Pop); // Pop the returned value
// Stack: []
setIl.Emit(OpCodes.Ret);

SharpLab - неоценимая помощь для написания IL.

...