2011-11-08 1 views
1

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

Я абсолютно уверен, что есть много подводных камней и сильное влияние на производительность, но это используется только для моего тестового кода - более конкретно, для создания mocks для нескольких интерфейсов.

Таким образом, в основном у меня есть класс (- см. Код ниже), который может создавать эти типы. Это очень хорошо подходит для «простых» типов. Но я хочу иметь возможность передавать и закрытые родовые типы. При попытке этого с моей текущей реализацией возникает TypeLoadException, когда я пытаюсь создать динамический тип (через TypeBuilder.CreateType).

Должен ли я указать TypeBuilder, что новый тип является общим типом, хотя он происходит только из закрытых родовых типов? Если да, как я могу это сделать?

Это, как я использую его:

MultiTypeBuilder multiTypeBuilder = 
    new MultiTypeBuilder(Guid.NewGuid().ToString()); 

multiTypeBuilder.SetBaseType(typeof(IFoo)); 
multiTypeBuilder.SetBaseType(typeof(IBar<IFoo>)); 
multiTypeBuilder.SetBaseType(typeof(IBaz)); 

Type multiType = multiTypeBuilder.CreateType(); 

Это моя текущая реализация (упрощенный немного):

private class MultiTypeBuilder 
{ 
    private readonly TypeBuilder m_TypeBuilder; 

    public MultiTypeBuilder(string name) 
    { 
     ModuleBuilder multiTypeModule = GetMultiTypeModule(); 

     TypeAttributes attributes = TypeAttributes.Interface | TypeAttributes.SpecialName | TypeAttributes.Abstract | TypeAttributes.Public; 

     m_TypeBuilder = multiTypeModule.DefineType(name, attributes); 
    } 

    public void SetBaseType(Type baseType) 
    { 
     m_TypeBuilder.AddInterfaceImplementation(baseType); 
    } 

    public Type CreateType() 
    { 
     return m_TypeBuilder.CreateType(); 
    } 

    private static ModuleBuilder GetMultiTypeModule() 
    { 
     AssemblyName assemblyName = new AssemblyName(c_DynamicAssemblyName); 
     AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
     return assemblyBuilder.DefineDynamicModule("MultiTypeAssembly"); 
    } 
} 

Спасибо за вашу поддержку!

+0

В вашем примере IBar определяется так, что T может быть только интерфейсом? Если нет, вы можете попробовать это. – Fantius

+0

Нет, это не так и не может быть, потому что я не знаю типы во время компиляции. –

+1

Компиляция вашего кода в LINQPad с пустым интерфейсом IBar, IBaz и IFoo работает правильно, не могли бы вы опубликовать какой-то код, который не работает? См. Вывод http://ideone.com/is5gU –

ответ

1

Позор мне.

Как VirtualBlackFox заявила, что мой слегка сжатый пример работает. В моем «реальном» коде я создаю имя динамического типа, комбинируя FullNames типов, которые необходимо объединить. Это явно приводит к недопустимому имени типа при использовании с типичными типами (я обнаружил, что, например, [ недействителен, что имеет смысл, если вы об этом подумаете).

Если я удаляю любые буквы, отличные от буквы или цифры, из сгенерированного имени, все работает как шарм.

Спасибо за ответы/комментарии!

1

Вам необходимо определить (виртуальные абстрактные) методы для интерфейсов, поскольку вы создаете абстрактный класс, а не интерфейс. Несколько исправлений:

  1. Когда вы перечисляете методы на интерфейсе, он не будет включать методы от унаследованных интерфейсов. Вам придется пройти унаследованные интерфейсы, а также интерфейсы, которые они наследуют, и так далее, рекурсивно, чтобы найти все методы, которые вам нужно определить.

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

  3. Если у вас есть два интерфейса, содержащие метод с одной и той же сигнатурой, вам нужно будет создать явные реализации. Они не могут быть абстрактными виртуальными (по крайней мере, если вы хотите следовать соглашениям C#), поэтому вам нужно будет испустить некоторый код IL, чтобы переслать какой-либо другой виртуальный абстрактный метод.


На самом деле, я только что заметил, что вы на самом деле не указано, что это класс в атрибутах типа. Вам нужно включить TypeAttributes.Class или TypeAttributes.Interface. В последнем случае вам не нужно выполнять всю описанную выше работу (и вам не понадобится Аннотация, если я правильно помню.)

+0

Спасибо за ответ Dan, но для простоты все типы, а также возвращаемый динамический тип являются интерфейсами. Соответственно, я обновил TypeAttributes. Но проблема все равно остается прежней. –

+0

@Florian, какой-либо интерфейс имеет внутреннюю видимость? Если это так, вам нужно добавить '[сборка: InternalsVisibleTo (/ * c_DynamicAssemblyName * /)]' в PropertyInfo.cs сборки, содержащей внутренние интерфейсы. –

+0

Да, я уже добавил атрибут. И это также происходит при использовании открытых интерфейсов ... –