【问题标题】:Create type "MyClass : OtherClass<MyClass> { }" at runtime?在运行时创建类型“MyClass:OtherClass<MyClass> {}”?
【发布时间】:2012-11-07 19:05:52
【问题描述】:

在 C# 中是否可以在运行时创建一个类型,该类型继承自一个泛型类,其中基类的模板参数是当前正在构造的类?这将编译得很好:

// I have this class:
public class OtherClass<T>
    where T : OtherClass<T>
{ }

// I want to create this at runtime:
public class MyClass : OtherClass<MyClass>
{ }

但我不确定如何使用System.Reflection.Emit.ModuleBuilder.TypeBuilder 创建MyClass:

AssemblyName asn = new AssemblyName("test.dll");
AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
    asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_assemblies");

ModuleBuilder = modb = asb.DefineDynamicModule("test", "test.dll");

TypeBuilder tb = modb.DefineType(
    "test",
    TypeAttributes.Public | TypeAttributes.Class,
    typeof(MyClass)); // How to specify inheritance?

// populate properties, fields, methods, etc., emit...

tb.CreateType();

这可能吗?

编辑 - 根据到目前为止的回复,我已经尝试过:

public class OtherClass<T>
    where T : OtherClass<T>
{ }

public static void CreateSimple()
{
    AssemblyName asn = new AssemblyName("test");
    AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
        asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_asms");
    ModuleBuilder modb = asb.DefineDynamicModule("test", "test.dll");

    try
    {
        TypeBuilder tb = modb.DefineType(
            "MyClass",
            TypeAttributes.Public | TypeAttributes.Class);

        Type my = tb.CreateType();
        tb.SetParent(typeof(OtherClass<>).MakeGenericType(my));

        my = tb.CreateType();
    }
    catch (Exception e)
    {
        throw;
    }
}

但得到这个异常:

GenericArguments[0], 'MyClass', on 'MyProject.Factory+OtherClass`1[T]'
violates the constraint of type 'T'.

【问题讨论】:

  • 但是……你为什么要这样做?
  • @LightStriker:因为 99% 的 MyClass 类型是在代码中定义的,但有一些需要在部署后创建(N​​Hibernate 模型)并且OtherClass 限制泛型类型是继承的类型来自OtherClass。
  • 我编辑了我的答案 - 现在可以使用,没有 GenericArgument 异常。

标签: c# generics reflection runtime


【解决方案1】:

编辑:这是我的最终工作答案:

        AssemblyName asn = new AssemblyName("test.dll");
        AssemblyBuilder asb = AppDomain.CurrentDomain.DefineDynamicAssembly(
            asn, AssemblyBuilderAccess.RunAndSave, @"D:\test_assemblies");

        ModuleBuilder modb = asb.DefineDynamicModule("test", "test.dll");

        TypeBuilder tb = modb.DefineType(
            "test",
            TypeAttributes.Public | TypeAttributes.Class);
        // Typebuilder is a sub class of Type
        tb.SetParent(typeof(OtherClass<>).MakeGenericType(tb));
        var t2 = tb.CreateType();
        var i = Activator.CreateInstance(t2);

诀窍是使用参数化的泛型类型调用 SetParent,参数是正在构造的类型本身的类型构建器。


使用TypeBuilder.SetParent(Type parent) 方法。

使用时要小心,异常抛出推迟到CreateType调用:

如果 parent 为 null,则使用 Object 作为基类型。

在 .NET Framework 1.0 和 1.1 版本中,如果 parent 是接口类型,则不会引发异常,但在调用 CreateType 方法时会引发 TypeLoadException。

SetParent 方法不检查大多数无效的父类型。例如,当当前类型有默认构造函数时,不拒绝没有默认构造函数的父类型,不拒绝密封类型,也不拒绝 Delegate 类型。在所有这些情况下,CreateType 方法都会引发异常。

要构建您的泛型类型OtherClass&lt;T&gt;,请使用MakeGenericType 方法:

var genericType = typeof(OtherClass<>).MakeGenericType(typeof(MyClass));

【讨论】:

  • 我已编辑我的帖子以包含此内容。不幸的是,我遇到了一个例外,即MyClass 违反了T 类型的约束。
【解决方案2】:

您可以在之后设置基本类型。

TypeBuilder.SetParent(Type)

【讨论】:

    【解决方案3】:

    是的。这是可能的。请参阅Reflection.Emit 命名空间和 TypeBuilder 的方法。

    【讨论】:

    • 他已经在使用 Reflection.Emit。你没看懂问题。
    猜你喜欢
    • 1970-01-01
    • 2013-02-01
    • 1970-01-01
    • 2010-12-13
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 1970-01-01
    相关资源
    最近更新 更多