Код Reflection.Emit вызывает base. вместо этого. на логическом поле

У меня есть следующий класс A.

public class A
{
    public string Name { get; set; }
}

Мне нужно создать динамический прокси-сервер, используя Reflection.Emit, чтобы переопределить Equals.

// This class must be generated by Reflection.Emit.
public class AProxy : A
{
    private bool equalsHasBeenCalled;

    public override bool Equals(object obj)
    {
        if (this.equalsHasBeenCalled)
        {
            return base.Equals(obj);
        }

        this.equalsHasBeenCalled = true;

        return CaseInsensitiveComparer.Equals(this, obj); // Demo.
    }
}

Однако фактический сгенерированный код (просматриваемый с помощью Reflector):

public class AProxy : A
{
    private bool equalsHasBeenCalled;

    public override bool Equals(object obj)
    {
        if (base.equalsHasBeenCalled)
        {
            return base.Equals(obj);
        }

        base.equalsHasBeenCalled = true;

        return CaseInsensitiveComparer.Equals(this, obj);
    }
}

.. что, конечно, вызывает исключение System.FieldAccessException (поскольку такого члена не существует). Правильно будет вызвать this.equalsHasBeenCalled (не base.equalsHasBeenCalled).

Я использую надстройку Reflection.Emit для Reflector для генерации кода (field1 — это FieldInfo для поля «equalsHasBeenCalled»):

        // Writing body
        gen.Emit(OpCodes.Nop);

        // I suspect it has to be around here.
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldfld, field1);
        gen.Emit(OpCodes.Ldc_I4_0);

        gen.Emit(OpCodes.Ceq);
        gen.Emit(OpCodes.Stloc_1);
        gen.Emit(OpCodes.Ldloc_1);
        gen.Emit(OpCodes.Brtrue_S, label25);
        gen.Emit(OpCodes.Nop);
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method2);
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Br_S, label42);
        gen.MarkLabel(label25);

        // ..and probably here also?
        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldc_I4_1);
        gen.Emit(OpCodes.Stfld, field1);

        gen.Emit(OpCodes.Ldarg_0);
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method3);
        gen.Emit(OpCodes.Stloc_0);
        gen.Emit(OpCodes.Br_S, label42);
        gen.MarkLabel(label42);
        gen.Emit(OpCodes.Ldloc_0);
        gen.Emit(OpCodes.Ret);

person Nikos Baxevanis    schedule 31.01.2012    source источник
comment
Итак, как вы видите этот вывод? Похоже на сломанный декомпилятор...   -  person leppie    schedule 31.01.2012
comment
Почему бы не показать нам свой собственный код для Reflection.Emit?   -  person leppie    schedule 31.01.2012
comment
Почему бы вам не написать то, что вам нужно на C#, декомпилировать в IL и посмотреть, как это настроено? Кроме того, если вам нужно сделать что-то подобное в вашем проекте, я предлагаю вам изучить Castle DynamicProxy.   -  person Nikola Radosavljević    schedule 31.01.2012
comment
@Никола Хорошее предложение! Спасибо. :)   -  person Nikos Baxevanis    schedule 31.01.2012
comment
@leppie Это все, что у меня есть. Я думаю, что мне нужно покопаться в IL вручную, как предложил Никола.   -  person Nikos Baxevanis    schedule 31.01.2012
comment
Что-то не так с FieldInfo. Вы получили его от TypeBuilder.DefineField? Покажите этот фрагмент.   -  person Hans Passant    schedule 31.01.2012
comment
Тогда я добавлю это как ответ :)   -  person Nikola Radosavljević    schedule 31.01.2012


Ответы (2)


Почему бы вам не написать то, что вам нужно на C#, декомпилировать в IL и посмотреть, как это настроено? Кроме того, если вам нужно сделать что-то подобное в вашем проекте, я предлагаю вам изучить Castle DynamicProxy.

person Nikola Radosavljević    schedule 31.01.2012
comment
Это было лучшее, что можно было сделать. Посмотрев на сгенерированный IL, я обнаружил, что сгенерированный код Reflection.Emit был правильным. Просто я предоставил FieldInfo для field1, в то время как я должен был предоставить экземпляр FieldBuilder для field1. Все работает. Глядя на IL было поучительно! - person Nikos Baxevanis; 31.01.2012

Возможно, ключевое слово this вызывает ошибку в вашей надстройке Reflection.Emit, из-за которой она генерирует неверный код. Попробуйте удалить его, так как на самом деле он ничего не делает, нет других equalsHasBeenCalled, которые нужно устранить с помощью ключевого слова this.

person Ben Robinson    schedule 31.01.2012
comment
Ха, this и base не существует в Иллинойсе. - person leppie; 31.01.2012
comment
@leppie Я предположил из его описания, что он компилировал код в своем первом примере в IL, а затем использовал надстройку для генерации кода Reflect.emit. - person Ben Robinson; 31.01.2012
comment
@BenRobinson Да, наверное.. :( - person Nikos Baxevanis; 31.01.2012