我有一个类,它看起来像这样:

public class Field
{
    public string FieldName;
    public string FieldType;
}

和一个对象列表<字段>值:

{"EmployeeID","int"},
{"EmployeeName","String"},
{"Designation","String"}

我想创建一个类,看起来像这样:

Class DynamicClass
{
    int EmployeeID,
    String EmployeeName,
    String Designation
}

有什么办法可以做到吗?

我希望它在运行时生成。我不希望在文件系统中驻留一个物理CS文件。


当前回答

我知道我重新打开这个旧任务,但使用c# 4.0这个任务是绝对无痛的。

dynamic expando = new ExpandoObject();
expando.EmployeeID=42;
expando.Designation="unknown";
expando.EmployeeName="curt"

//or more dynamic
AddProperty(expando, "Language", "English");

更多信息请参见 https://www.oreilly.com/learning/building-c-objects-dynamically

其他回答

根据@danijels的回答,在VB中动态创建一个类。NET:

Imports System.Reflection
Imports System.Reflection.Emit

Public Class ObjectBuilder

Public Property myType As Object
Public Property myObject As Object

Public Sub New(fields As List(Of Field))
    myType = CompileResultType(fields)
    myObject = Activator.CreateInstance(myType)
End Sub

Public Shared Function CompileResultType(fields As List(Of Field)) As Type
    Dim tb As TypeBuilder = GetTypeBuilder()
    Dim constructor As ConstructorBuilder = tb.DefineDefaultConstructor(MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.RTSpecialName)

    For Each field In fields
        CreateProperty(tb, field.Name, field.Type)
    Next

    Dim objectType As Type = tb.CreateType()
    Return objectType
End Function

Private Shared Function GetTypeBuilder() As TypeBuilder
    Dim typeSignature = "MyDynamicType"
    Dim an = New AssemblyName(typeSignature)
    Dim assemblyBuilder As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run)
    Dim moduleBuilder As ModuleBuilder = assemblyBuilder.DefineDynamicModule("MainModule")
    Dim tb As TypeBuilder = moduleBuilder.DefineType(typeSignature, TypeAttributes.[Public] Or TypeAttributes.[Class] Or TypeAttributes.AutoClass Or TypeAttributes.AnsiClass Or TypeAttributes.BeforeFieldInit Or TypeAttributes.AutoLayout, Nothing)
    Return tb
End Function

Private Shared Sub CreateProperty(tb As TypeBuilder, propertyName As String, propertyType As Type)
    Dim fieldBuilder As FieldBuilder = tb.DefineField("_" & propertyName, propertyType, FieldAttributes.[Private])

    Dim propertyBuilder As PropertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, Nothing)
    Dim getPropMthdBldr As MethodBuilder = tb.DefineMethod("get_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, propertyType, Type.EmptyTypes)
    Dim getIl As ILGenerator = getPropMthdBldr.GetILGenerator()

    getIl.Emit(OpCodes.Ldarg_0)
    getIl.Emit(OpCodes.Ldfld, fieldBuilder)
    getIl.Emit(OpCodes.Ret)

    Dim setPropMthdBldr As MethodBuilder = tb.DefineMethod("set_" & propertyName, MethodAttributes.[Public] Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig, Nothing, {propertyType})

    Dim setIl As ILGenerator = setPropMthdBldr.GetILGenerator()
    Dim modifyProperty As Label = setIl.DefineLabel()
    Dim exitSet As Label = setIl.DefineLabel()

    setIl.MarkLabel(modifyProperty)
    setIl.Emit(OpCodes.Ldarg_0)
    setIl.Emit(OpCodes.Ldarg_1)
    setIl.Emit(OpCodes.Stfld, fieldBuilder)

    setIl.Emit(OpCodes.Nop)
    setIl.MarkLabel(exitSet)
    setIl.Emit(OpCodes.Ret)

    propertyBuilder.SetGetMethod(getPropMthdBldr)
    propertyBuilder.SetSetMethod(setPropMthdBldr)
End Sub

End Class

运行时代码生成与JVM和CLR - 彼得Sestoft

为真正对这类编程感兴趣的人工作。

我给你的建议是,如果你声明了一些东西,尽量避免字符串,所以如果你有类Field,最好使用类System。类型来存储字段类型而不是字符串。 为了最好的解决方案,而不是创建新的类,尝试使用那些已经创建的FiledInfo,而不是创建新的。

您可以考虑使用动态模块和类来完成这项工作。唯一的缺点是它仍然加载在应用程序域中。但是。net 4.0支持可收集的动态程序集,因此你可以动态地重新创建类/类型。

你可以使用CSharpProvider:

var code = @"
    public class Abc {
       public string Get() { return ""abc""; }
    }
";

var options = new CompilerParameters();
options.GenerateExecutable = false;
options.GenerateInMemory = false;

var provider = new CSharpCodeProvider();
var compile = provider.CompileAssemblyFromSource(options, code);

var type = compile.CompiledAssembly.GetType("Abc");
var abc = Activator.CreateInstance(type);

var method = type.GetMethod("Get");
var result = method.Invoke(abc, null);

Console.WriteLine(result); //output: abc

这需要一些工作,但肯定不是不可能的。

我所做的是:

在字符串中创建c#源代码(不需要写入文件), 通过Microsoft.CSharp.CSharpCodeProvider (CompileAssemblyFromSource)运行它 查找生成的类型 并创建该类型的实例(Activator.CreateInstance)

这样您就可以处理已经知道的c#代码,而不必发出MSIL。

但是,如果你的类实现了一些接口(或从一些基类派生),这是最好的工作,否则调用代码(阅读:编译器)如何知道将在运行时生成的类?