Reputation: 14786
I am trying to dynamically combine any number of types to a new type that inherits all of those types. For now it should only work for interfaces.
I am absolutely aware that there are many pitfalls and a heavy performance impact, but this is only used for my test code - to be more specific, for creating mocks for multiple interfaces.
So, basically I have a class (TypeBuilder
- see code below) that is able to create those types. It works out pretty good for "simple" types. But I want to be able to pass it closed generic types as well. When trying to this with my current implementation, a TypeLoadException
is thrown when I am trying to create the dynamic type (via TypeBuilder.CreateType
).
Do I have to tell the TypeBuilder
that the new type is a generic type, although it only derives from closed generic types? If yes, how can I do this?
This is how I use it:
MultiTypeBuilder multiTypeBuilder =
new MultiTypeBuilder(Guid.NewGuid().ToString());
multiTypeBuilder.SetBaseType(typeof(IFoo));
multiTypeBuilder.SetBaseType(typeof(IBar<IFoo>));
multiTypeBuilder.SetBaseType(typeof(IBaz));
Type multiType = multiTypeBuilder.CreateType();
This is my current implementation (simplified a little bit):
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");
}
}
Thanks for your support!
Upvotes: 1
Views: 466
Reputation: 14786
Shame on me.
As VirtualBlackFox stated my slightly condensed example works. In my "real" code I create the name of the dynamic type by combining the FullNames
of the types that need to be combined. This obviously produces an invalid typename when used with generic types (I found out that e.g. [
is invalid, which makes sense if you think about it).
If I remove any non-letter-or-digit characters from the generated typename, everything works like a charm.
Thanks for the answers / comments!
Upvotes: 1
Reputation: 27495
You need to define the (virtual abstract) methods for the interfaces, as you're creating an abstract class, not an interface. A few gotchas:
When you enumerate the methods on an interface, it will not include methods from inherited interfaces. You'll have to walk the inherited interfaces, plus the interfaces they inherit and so on, recursively, in order to find all of the methods you need to define.
When you walk the interfaces, you'll need to handle the case when two interfaces inherit from the same interface, so that you don't try to create the method twice.
In the event where you have two interfaces containing a method with the same signature, you will need to create explicit implementations. These cannot be abstract virtual (at least if you want to follow C# conventions), so you'll need to emit some IL code to forward to some other virtual abstract method.
Actually, I just noticed that you didn't actually specify that it is a Class in the type attributes. You need to include TypeAttributes.Class or TypeAttributes.Interface. In the latter case, you don't need to do all of the above work (and you won't need Abstract, if I recall correctly.)
Upvotes: 1