Исключение Acess системного поля из производного класса Reflection.Emit - PullRequest
0 голосов
/ 27 сентября 2018

У меня есть динамически сгенерированный класс, которому требуется доступ к полю из класса base.Я продолжаю получать это исключение:

Исключение

System.FieldAccessException: 'Попытка методом' AutoGenRelay.SetAsync (System.String, redius.Cell, System.Nullable`1, redius.SetPolicy) 'для доступа к полю' RediusTests.DaemonBase.IsTransaction 'не удалось.'

Базовый класс :

public abstract class DaemonBase : Abs.Api.IAll,IDisposable {

        #region IAll implementation
         .......
        public abstract Task<Pair> BLPopAsync(IEnumerable<string> keys, TimeSpan? timeout);
          ......
        #endregion

        public bool IsTransaction;

        public DaemonBase() {

        }
        public void Dispose() {
            throw new NotImplementedException();
        }
    }

Класс сгенерированный автоматически

class RelayGen : DaemonBase {
        public override Task<Pair> BLPopAsync(IEnumerable<string> keys, TimeSpan? timeout) {
            if (this.IsTransaction) {
               return this.relay.normal.BLPopAsync(keys, timeout);
            } else
                return this.relay.tran.BLPopAsync(keys, timeout);
        }

Как получить доступ к FieldInfo из IsTransaction:

private static FieldInfo isTran = typeof(DaemonBase).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance )
                .First(x => x.Name == "IsTransaction");


   private void DefineMethod(MethodInfo abstractMethod) {

    Type essentialReturnType = abstractMethod.ReturnType.GetGenericArguments()[0];
    ParameterInfo[] parameters = abstractMethod.GetParameters();
    MethodBuilder newMethod = this.typeBuilder.DefineMethod(
        abstractMethod.Name,
        attributes: MethodAttributes.Public | MethodAttributes.Virtual,
        returnType: abstractMethod.ReturnType,
        parameterTypes: parameters.Select(par => par.ParameterType).ToArray()
    );
    this.typeBuilder.DefineMethodOverride(newMethod, abstractMethod);

    MethodInfo rediusMethod = typeof(OpsAbs)
        .GetMethods(BindingFlags.Public | BindingFlags.Instance)
        .Where(x => x.Name == newMethod.Name).First();

    ILGenerator ilgen = newMethod.GetILGenerator();
    Label falseLabel = ilgen.DefineLabel();

    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldfld, isTran);
    ilgen.Emit(OpCodes.Brfalse, falseLabel);  //branching

    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldfld, relay);
    ilgen.Emit(OpCodes.Ldfld, normalField);

    for (int argIndex = 1; argIndex <= parameters.Length; argIndex++) {
        ilgen.Emit(OpCodes.Ldarg, argIndex);
    }
    ilgen.Emit(OpCodes.Callvirt, rediusMethod);
    ilgen.Emit(OpCodes.Ret);


    ilgen.MarkLabel(falseLabel);  //false branch
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Ldfld, relay);
    ilgen.Emit(OpCodes.Ldfld, tranField);

    for (int argIndex = 1; argIndex <= parameters.Length; argIndex++) {
        ilgen.Emit(OpCodes.Ldarg, argIndex);
    }
    ilgen.Emit(OpCodes.Callvirt, rediusMethod);
    ilgen.Emit(OpCodes.Ret);
}

PS Я знаю, что не показывал много кода, но я пытаюсь понять, почему производный класс (Reflection.Emit сгенерированный) не может получить доступ к внутреннему полю базового класса?

IsTransaction является внутренним для DaemonBase.Почему я не могу получить к нему доступ из производного класса?

PS 2 Я обновил код методом. Я не предоставил реализацию OpsAbs, так как он используется после того, как яиспользуйте IsTransaction поле имплозии.

1 Ответ

0 голосов
/ 27 сентября 2018

Ваша ошибка должна быть где-то в коде, который вы не показывали.Взгляните на этот пример:

// create a TypeBuilder
var relayGenBuilder = module.DefineType("RelayGen", TypeAttributes.Public, typeof(DaemonBase));

var methodAttributes = MethodAttributes.Public
    | MethodAttributes.Virtual
    | MethodAttributes.HideBySig;
var returnType = typeof(Task<>).MakeGenericType(typeof(Pair));
var parameters = new []
{
    typeof(IEnumerable<>).MakeGenericType(typeof(string)),
    typeof(Nullable<>).MakeGenericType(typeof(TimeSpan))
};

// cteate a new method
var popAsync = relayGenBuilder.DefineMethod("BLPopAsync", 
    methodAttributes, 
    CallingConventions.HasThis,
    returnType,
    parameters); 

var ilGen = popAsync.GetILGenerator();

var falseLabel = ilGen.DefineLabel();
var isTran = typeof(DaemonBase).GetField("IsTransaction");
var writeLine = typeof(Console).GetMethod("WriteLine", new[] {typeof(string)});

ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldfld, isTran);
ilGen.Emit(OpCodes.Brfalse_S, falseLabel);
// if (IsTransaction)
    ilGen.Emit(OpCodes.Ldstr, "IsTransaction equals true");
    ilGen.EmitCall(OpCodes.Call, writeLine, null);
    ilGen.Emit(OpCodes.Ldnull);
    ilGen.Emit(OpCodes.Ret);
// else
    ilGen.MarkLabel(falseLabel);
    ilGen.Emit(OpCodes.Ldnull);
    ilGen.Emit(OpCodes.Ret);

Не забудьте пометить новый метод как override:

relayGenBuilder.DefineMethodOverride(popAsync, typeof(DaemonBase).GetMethod("BLPopAsync"));

Этот код создаст метод, который выглядит как

public override Task<Pair> BLPopAsync(IEnumerable<string> keys, TimeSpan? timeout)
{
    if (IsTransaction)
        Console.WriteLine("IsTransaction equals true");

    return null;
}
...