有没有方法在运行时构建新类型?

26

我将会问一个可能听起来很奇怪的问题。

有没有一种方法可以在运行时构建一个新的类?或者至少,向现有类添加一个新属性。

我的意思是创建一个不存在的类,而不是现有类的实例。稍后我可以使用反射来加载和使用这个类。


请参考以下链接:https://dev59.com/SG865IYBdhLWcg3wWtKz#3862241 - CAD bloke
4个回答

31

无法向现有类型添加属性,但您可以使用Reflection.Emit在运行时创建新类型。这是相当复杂的内容,大致步骤如下:

AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(
      assemblyName , AssemblyBuilderAccess.Run, assemblyAttributes);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("ModuleName");
TypeBuilder typeBuilder = moduleBuilder.DefineType(
      "MyNamespace.TypeName" , TypeAttributes.Public);

typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);

// Add a method
newMethod = typeBuilder.DefineMethod("MethodName" , MethodAttributes.Public);

ILGenerator ilGen = newMethod.GetILGenerator();

// Create IL code for the method
ilGen.Emit(...);

// ...

// Create the type itself
Type newType = typeBuilder.CreateType();

这段代码仅仅是一个示例。它可能包含错误。

你也可以使用System.CodeDom在运行时编译C#源代码来生成类,但我对此了解不多。


抱歉,m rwwilden 标记了这个答案,因为这个答案付出了更多的努力(尽管你们两个都提供了相同的解决方案)。 - user88637
1
没问题。我同意这个答案比我的更详细一些。 - Ronald Wildenberg

5

这不是一个奇怪的问题 - 在某些情况下,它可能非常有用。例如,我有时会在性能测试中使用这种技术:

public static Type[] DynamicTypes;

public void CreateObjects()
{
  var codeNamespace = new CodeNamespace( "DynamicClasses" );
  codeNamespace.Imports.Add( new CodeNamespaceImport( "System" ) );
  codeNamespace.Imports.Add( new CodeNamespaceImport( "System.ComponentModel" ) );

  for( var i = 0; i < 2000; i++ )
  {
    var classToCreate = new CodeTypeDeclaration( "DynamicClass_" + i )
    {
      TypeAttributes = TypeAttributes.Public
    };
    var codeConstructor1 = new CodeConstructor
    {
      Attributes = MemberAttributes.Public
    };
    classToCreate.Members.Add( codeConstructor1 );

    codeNamespace.Types.Add( classToCreate );
  }

  var codeCompileUnit = new CodeCompileUnit();
  codeCompileUnit.Namespaces.Add( codeNamespace );

  var compilerParameters = new CompilerParameters
  {
    GenerateInMemory = true,
    IncludeDebugInformation = true,
    TreatWarningsAsErrors = true,
    WarningLevel = 4
  };
  compilerParameters.ReferencedAssemblies.Add( "System.dll" );

  var compilerResults = new CSharpCodeProvider().CompileAssemblyFromDom( compilerParameters, codeCompileUnit );

  if( compilerResults == null )
  {
    throw new InvalidOperationException( "ClassCompiler did not return results." );
  }
  if( compilerResults.Errors.HasErrors )
  {
    var errors = string.Empty;
    foreach( CompilerError compilerError in compilerResults.Errors )
    {
      errors += compilerError.ErrorText + "\n";
    }
    Debug.Fail( errors );
    throw new InvalidOperationException( "Errors while compiling the dynamic classes:\n" + errors );
  }

  var dynamicAssembly = compilerResults.CompiledAssembly;
  DynamicTypes = dynamicAssembly.GetExportedTypes();
}

5

请查看System.Reflection.Emit命名空间。虽然我从未使用过它,但该命名空间中的类可用于生成IL(中间语言)。


4
您可以查看System.CodeDom命名空间。根据其中一个链接页面上的说法:
.NET Framework包括一种称为代码文档对象模型(CodeDOM)的机制,它使得那些生成多种编程语言源代码的程序开发人员能够在运行时基于单个表示要呈现的代码的模型生成源代码。
我并不是专家,只是记得在我的.NET Framework海报上看到过它。 :)
编辑:自回答以来,我已经玩了一下System.CodeDom。我写了一个使用一些基本CodeDom的博客文章,这可能有助于那些想要开始使用它的人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接