我有一个类,它看起来像这样:
public class Field
{
public string FieldName;
public string FieldType;
}
和一个对象列表<字段>值:
{"EmployeeID","int"},
{"EmployeeName","String"},
{"Designation","String"}
我想创建一个类,看起来像这样:
Class DynamicClass
{
int EmployeeID,
String EmployeeName,
String Designation
}
有什么办法可以做到吗?
我希望它在运行时生成。我不希望在文件系统中驻留一个物理CS文件。
是的,您可以为此使用System.Reflection.Emit命名空间。如果你没有经验,这并不直接,但这当然是可能的。
编辑:这段代码可能有缺陷,但它会给你一个大概的想法,希望能有一个良好的开端。
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace TypeBuilderNamespace
{
public static class MyTypeBuilder
{
public static void CreateNewObject()
{
var myType = CompileResultType();
var myObject = Activator.CreateInstance(myType);
}
public static Type CompileResultType()
{
TypeBuilder tb = GetTypeBuilder();
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
// NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
foreach (var field in yourListOfFields)
CreateProperty(tb, field.FieldName, field.FieldType);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder()
{
var typeSignature = "MyDynamicType";
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
}
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = 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);
}
}
}
哇!谢谢你的回答!我添加了一些功能,以创建一个“数据表到json”的转换器,我与您分享。
Public Shared Sub dt2json(ByVal _dt As DataTable, ByVal _sb As StringBuilder)
Dim t As System.Type
Dim oList(_dt.Rows.Count - 1) As Object
Dim jss As New JavaScriptSerializer()
Dim i As Integer = 0
t = CompileResultType(_dt)
For Each dr As DataRow In _dt.Rows
Dim o As Object = Activator.CreateInstance(t)
For Each col As DataColumn In _dt.Columns
setvalue(o, col.ColumnName, dr.Item(col.ColumnName))
Next
oList(i) = o
i += 1
Next
jss = New JavaScriptSerializer()
jss.Serialize(oList, _sb)
End Sub
在"compileresulttype" sub中,我修改了:
For Each column As DataColumn In _dt.Columns
CreateProperty(tb, column.ColumnName, column.DataType)
Next
Private Shared Sub setvalue(ByVal _obj As Object, ByVal _propName As String, ByVal _propValue As Object)
Dim pi As PropertyInfo
pi = _obj.GetType.GetProperty(_propName)
If pi IsNot Nothing AndAlso pi.CanWrite Then
If _propValue IsNot DBNull.Value Then
pi.SetValue(_obj, _propValue, Nothing)
Else
Select Case pi.PropertyType.ToString
Case "System.String"
pi.SetValue(_obj, String.Empty, Nothing)
Case Else
'let the serialiser use javascript "null" value.
End Select
End If
End If
End Sub
根据@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
您还可以使用DynamicObject动态创建类。
public class DynamicClass : DynamicObject
{
private Dictionary<string, KeyValuePair<Type, object>> _fields;
public DynamicClass(List<Field> fields)
{
_fields = new Dictionary<string, KeyValuePair<Type, object>>();
fields.ForEach(x => _fields.Add(x.FieldName,
new KeyValuePair<Type, object>(x.FieldType, null)));
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (_fields.ContainsKey(binder.Name))
{
var type = _fields[binder.Name].Key;
if (value.GetType() == type)
{
_fields[binder.Name] = new KeyValuePair<Type, object>(type, value);
return true;
}
else throw new Exception("Value " + value + " is not of type " + type.Name);
}
return false;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _fields[binder.Name].Value;
return true;
}
}
我将所有类字段连同它们的类型和值一起存储在字典_fields中。这两种方法都可以获取或设置一些属性的值。必须使用dynamic关键字创建该类的实例。
使用你的例子:
var fields = new List<Field>() {
new Field("EmployeeID", typeof(int)),
new Field("EmployeeName", typeof(string)),
new Field("Designation", typeof(string))
};
dynamic obj = new DynamicClass(fields);
//set
obj.EmployeeID = 123456;
obj.EmployeeName = "John";
obj.Designation = "Tech Lead";
obj.Age = 25; //Exception: DynamicClass does not contain a definition for 'Age'
obj.EmployeeName = 666; //Exception: Value 666 is not of type String
//get
Console.WriteLine(obj.EmployeeID); //123456
Console.WriteLine(obj.EmployeeName); //John
Console.WriteLine(obj.Designation); //Tech Lead
编辑:下面是我的类Field的外观:
public class Field
{
public Field(string name, Type type)
{
this.FieldName = name;
this.FieldType = type;
}
public string FieldName;
public Type FieldType;
}
您还可以使用DynamicExpressions动态地创建类。
因为'Dictionary'有紧凑的初始化器并处理键冲突,所以你会想这样做。
var list = new Dictionary<string, string> {
{
"EmployeeID",
"int"
}, {
"EmployeeName",
"String"
}, {
"Birthday",
"DateTime"
}
};
或者,您可能希望使用JSON转换器将序列化的字符串对象构造为可管理的对象。
然后使用System.Linq.Dynamic;
IEnumerable<DynamicProperty> props = list.Select(property => new DynamicProperty(property.Key, Type.GetType(property.Value))).ToList();
Type t = DynamicExpression.CreateClass(props);
剩下的就是使用System.Reflection。
object obj = Activator.CreateInstance(t);
t.GetProperty("EmployeeID").SetValue(obj, 34, null);
t.GetProperty("EmployeeName").SetValue(obj, "Albert", null);
t.GetProperty("Birthday").SetValue(obj, new DateTime(1976, 3, 14), null);
}
对于那些想要创建一个动态类的属性(即POCO),并创建这个类的列表。使用后面提供的代码,这将创建一个动态类,并创建一个该类的列表。
var properties = new List<DynamicTypeProperty>()
{
new DynamicTypeProperty("doubleProperty", typeof(double)),
new DynamicTypeProperty("stringProperty", typeof(string))
};
// create the new type
var dynamicType = DynamicType.CreateDynamicType(properties);
// create a list of the new type
var dynamicList = DynamicType.CreateDynamicList(dynamicType);
// get an action that will add to the list
var addAction = DynamicType.GetAddAction(dynamicList);
// call the action, with an object[] containing parameters in exact order added
addAction.Invoke(new object[] {1.1, "item1"});
addAction.Invoke(new object[] {2.1, "item2"});
addAction.Invoke(new object[] {3.1, "item3"});
下面是前面代码使用的类。
注意:你还需要引用Microsoft.CodeAnalysis.CSharp库。
/// <summary>
/// A property name, and type used to generate a property in the dynamic class.
/// </summary>
public class DynamicTypeProperty
{
public DynamicTypeProperty(string name, Type type)
{
Name = name;
Type = type;
}
public string Name { get; set; }
public Type Type { get; set; }
}
public static class DynamicType
{
/// <summary>
/// Creates a list of the specified type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<object> CreateDynamicList(Type type)
{
var listType = typeof(List<>);
var dynamicListType = listType.MakeGenericType(type);
return (IEnumerable<object>) Activator.CreateInstance(dynamicListType);
}
/// <summary>
/// creates an action which can be used to add items to the list
/// </summary>
/// <param name="listType"></param>
/// <returns></returns>
public static Action<object[]> GetAddAction(IEnumerable<object> list)
{
var listType = list.GetType();
var addMethod = listType.GetMethod("Add");
var itemType = listType.GenericTypeArguments[0];
var itemProperties = itemType.GetProperties();
var action = new Action<object[]>((values) =>
{
var item = Activator.CreateInstance(itemType);
for(var i = 0; i < values.Length; i++)
{
itemProperties[i].SetValue(item, values[i]);
}
addMethod.Invoke(list, new []{item});
});
return action;
}
/// <summary>
/// Creates a type based on the property/type values specified in the properties
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static Type CreateDynamicType(IEnumerable<DynamicTypeProperty> properties)
{
StringBuilder classCode = new StringBuilder();
// Generate the class code
classCode.AppendLine("using System;");
classCode.AppendLine("namespace Dexih {");
classCode.AppendLine("public class DynamicClass {");
foreach (var property in properties)
{
classCode.AppendLine($"public {property.Type.Name} {property.Name} {{get; set; }}");
}
classCode.AppendLine("}");
classCode.AppendLine("}");
var syntaxTree = CSharpSyntaxTree.ParseText(classCode.ToString());
var references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(DictionaryBase).GetTypeInfo().Assembly.Location)
};
var compilation = CSharpCompilation.Create("DynamicClass" + Guid.NewGuid() + ".dll",
syntaxTrees: new[] {syntaxTree},
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms);
if (!result.Success)
{
var failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
var message = new StringBuilder();
foreach (var diagnostic in failures)
{
message.AppendFormat("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
throw new Exception($"Invalid property definition: {message}.");
}
else
{
ms.Seek(0, SeekOrigin.Begin);
var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
var dynamicType = assembly.GetType("Dexih.DynamicClass");
return dynamicType;
}
}
}
}
正如Hans所建议的,您可以使用Roslyn动态创建类。
完整的资料来源:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace RoslynDemo1
{
class Program
{
static void Main(string[] args)
{
var fields = new List<Field>()
{
new Field("EmployeeID","int"),
new Field("EmployeeName","String"),
new Field("Designation","String")
};
var employeeClass = CreateClass(fields, "Employee");
dynamic employee1 = Activator.CreateInstance(employeeClass);
employee1.EmployeeID = 4213;
employee1.EmployeeName = "Wendy Tailor";
employee1.Designation = "Engineering Manager";
dynamic employee2 = Activator.CreateInstance(employeeClass);
employee2.EmployeeID = 3510;
employee2.EmployeeName = "John Gibson";
employee2.Designation = "Software Engineer";
Console.WriteLine($"{employee1.EmployeeName}");
Console.WriteLine($"{employee2.EmployeeName}");
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
public static Type CreateClass(List<Field> fields, string newClassName, string newNamespace = "Magic")
{
var fieldsCode = fields
.Select(field => $"public {field.FieldType} {field.FieldName};")
.ToString(Environment.NewLine);
var classCode = $@"
using System;
namespace {newNamespace}
{{
public class {newClassName}
{{
public {newClassName}()
{{
}}
{fieldsCode}
}}
}}
".Trim();
classCode = FormatUsingRoslyn(classCode);
var assemblies = new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
};
/*
var assemblies = AppDomain
.CurrentDomain
.GetAssemblies()
.Where(a => !string.IsNullOrEmpty(a.Location))
.Select(a => MetadataReference.CreateFromFile(a.Location))
.ToArray();
*/
var syntaxTree = CSharpSyntaxTree.ParseText(classCode);
var compilation = CSharpCompilation
.Create(newNamespace)
.AddSyntaxTrees(syntaxTree)
.AddReferences(assemblies)
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms);
//compilation.Emit($"C:\\Temp\\{newNamespace}.dll");
if (result.Success)
{
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
var newTypeFullName = $"{newNamespace}.{newClassName}";
var type = assembly.GetType(newTypeFullName);
return type;
}
else
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return null;
}
}
}
public static string FormatUsingRoslyn(string csCode)
{
var tree = CSharpSyntaxTree.ParseText(csCode);
var root = tree.GetRoot().NormalizeWhitespace();
var result = root.ToFullString();
return result;
}
}
public class Field
{
public string FieldName;
public string FieldType;
public Field(string fieldName, string fieldType)
{
FieldName = fieldName;
FieldType = fieldType;
}
}
public static class Extensions
{
public static string ToString(this IEnumerable<string> list, string separator)
{
string result = string.Join(separator, list);
return result;
}
}
}