我有一个类,它看起来像这样:
public class Field
{
public string FieldName;
public string FieldType;
}
和一个对象列表<字段>值:
{"EmployeeID","int"},
{"EmployeeName","String"},
{"Designation","String"}
我想创建一个类,看起来像这样:
Class DynamicClass
{
int EmployeeID,
String EmployeeName,
String Designation
}
有什么办法可以做到吗?
我希望它在运行时生成。我不希望在文件系统中驻留一个物理CS文件。
对于那些想要创建一个动态类的属性(即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;
}
}
}
}
是的,您可以为此使用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);
}
}
}