Создание DynamicType в .NET, реализующего интерфейс, но использующего реализации членов из базового класса

Я пытаюсь создать динамический класс, реализующий интерфейс, но в котором один или несколько членов уже существуют в базе. Я скомпилировал следующий код на C # и изучил его в отражателе, чтобы узнать, что делает компилятор C #.

class BaseClass
{
    public string Bob
    {
        get { return "Bob"; }
    }
}

interface IStuff
{
    string Bob { get; }
}

class SubClass : BaseClass, IStuff
{
}

Reflector не показывает никакой реализации в SubClass.

.class private auto ansi beforefieldinit SubClass
    extends Enterprise.Services.OperationalActions.Business.Filters.BaseClass
    implements Enterprise.Services.OperationalActions.Business.Filters.IStuff
{
}

Но если я не испущу член явно, TypeBuilder.CreateType() выдает InvalidOperationException, что член не имеет реализации. Итак, у меня вопрос: как мне сказать TypeBuilder, что член интерфейса должен взять свою реализацию из базы?


person Brian Reichle    schedule 26.01.2011    source источник
comment
В вашем примере кода не показано, что SubClass реализует IStuff. Вы хотели написать Class SubClass : BaseClass, IStuff?   -  person Jim Mischel    schedule 26.01.2011


Ответы (2)


Похоже, что с TypeBuilder вам придется добавить частную сквозную передачу, просто чтобы сделать ее счастливой (ниже). Вы также можете попробовать использовать конструктор IKVM - почти идентичный API, но он может не иметь этого ограничения.

using System;
using System.Reflection;
using System.Reflection.Emit;
public class BaseClass
{
    public string Bob
    {
        get { return "Bob"; }
    }
}

public interface IStuff
{
    string Bob { get; }
}
static class Program
{
    static void Main()
    {
        var name = new AssemblyName("foo");
        var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
        var mod = asm.DefineDynamicModule("foo");
        var parent = typeof(BaseClass);
        var type = mod.DefineType("SubClass", parent.Attributes, parent);
        type.AddInterfaceImplementation(typeof(IStuff));

        var bob_get = type.DefineMethod("bob_get", MethodAttributes.Virtual | MethodAttributes.Private,
            typeof(string), Type.EmptyTypes);
        var il = bob_get.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.EmitCall(OpCodes.Callvirt, parent.GetProperty("Bob").GetGetMethod(), null);
        il.Emit(OpCodes.Ret);
        type.DefineMethodOverride(bob_get, typeof(IStuff).GetProperty("Bob").GetGetMethod());
        var final = type.CreateType();
        IStuff obj = (IStuff) Activator.CreateInstance(final);
        Console.WriteLine(obj.Bob);
    }
}
person Marc Gravell    schedule 26.01.2011
comment
Спасибо, к сожалению, мне нужно свойство оболочки, но этого должно хватить для моих нужд. - person Brian Reichle; 28.01.2011

Компилятор C # фактически генерирует другой код для BaseType в зависимости от того, находится ли ваше SubClass определение в той же сборке или нет. Итак, если у вас есть только это:

interface IStuff
{
    string Bob { get; }
}

public class BaseClass
{
    public string Bob
    {
        get { return "Bob"; }
    }
}

а затем определите SubClass в другом проекте C #, тогда компилятор фактически выдаст в нем явную реализацию интерфейса. Это связано с тем, что в этом случае BaseClass.get_Bob будет определен как не виртуальный, что означает, что его нельзя использовать для выполнения контракта интерфейса.

См. Также Почему методы интерфейса C # не объявлен абстрактным или виртуальным?, в котором эта странность явно обсуждается в конце ответа.

person kvb    schedule 26.01.2011
comment
Я бы подумал, что запись vtable для интерфейса просто укажет на реализацию BaseClass. Мне придется перейти по ссылке, когда я вернусь домой сегодня вечером, похоже, это может быть интересно. - person Brian Reichle; 28.01.2011
comment
@Brian - Я считаю, что только виртуальные методы появляются в согласованных слотах в таблицах методов подклассов. Итак, чтобы интерфейс указывал на слот в таблице методов BaseClass, которая также будет работать для подклассов, метод должен быть виртуальным, поэтому все реализации методов интерфейса в IL должны быть виртуальными (хотя компилятор C # не требует, чтобы вы аннотировали их как таковые). - person kvb; 28.01.2011