有没有办法在运行时构建一个新类型?

时间:2009-05-30 08:35:27

标签: c# .net vb.net reflection

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

有没有办法在运行时构建新类?或者至少将新属性添加到现有类中。

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

4 个答案:

答案 0 :(得分:24)

无法将属性添加到现有类型,但您可以使用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#源代码来生成类,但我对此并不了解。

答案 1 :(得分:5)

查看System.Reflection.Emit命名空间。我自己从未使用它,但此命名空间中的类可用于生成IL(中间语言)。

答案 2 :(得分: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();
}

答案 3 :(得分:3)

您可以查看System.CodeDom命名空间。根据其中一个页面链接:

  

.NET Framework包含一种称为代码文档对象模型(CodeDOM)的机制,它使发出源代码的程序的开发人员能够在运行时以多种编程语言生成源代码,基于表示代码的单个模型。渲染。

我不是这方面的专家,我只记得在我墙上的.NET Framework海报上看到它。 :)

编辑:自写这个答案以来,我已经使用了System.CodeDom了。我写了一个blog post,它使用了一些基本的CodeDom,可以帮助那些想要开始使用它的人。