为运行时类C#创建带有未知参数列表的动态构造函数

时间:2018-11-28 02:49:18

标签: c# dynamic

我正在尝试处理具有未知类型的数据集。为此,我要根据显示类格式的头文件创建运行时类。头文件的格式如下:

valueOneName valueTwoName valueThreeName etc...

数据的格式化方法相同,但使用值而不是变量名。我试图做的是基于头文件创建一个运行时类,然后使用来自不同文件的数据创建该类的实例。以下是相关代码。

 static readonly char[] delimeters = new char[] { '\t' };

public static object CreateObject(Type type, params object[] args)
{
    return Activator.CreateInstance(type, args);
}

public static Type CompileType(string typeName, string typeData)
{
    string[] splitTypeData = typeData.Split(delimeters);
    TypeBuilder tb = GetTypeBuilder(typeName);
    CreateConstructor(tb, splitTypeData);
    foreach (string s in splitTypeData)
        CreateProperty(tb, s, typeof(string));

    Type objectType = tb.CreateType();
    return objectType;
}


private static void CreateConstructor(TypeBuilder tb, string[] splitTypeData)
{
    GenericTypeParameterBuilder[] types = tb.DefineGenericParameters(splitTypeData);
    Type[] t = new Type[types.Length];
    for (int i = 0; i < t.Length; i++)
    {
        t[i] = types[i];//.GetGenericArguments()[0];//.GetGenericTypeDefinition();
    }
    ConstructorBuilder constructor = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
        CallingConventions.Standard, t);
    ConstructorInfo conObj = typeof(object).GetConstructor(t);

    ILGenerator il = constructor.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Call, conObj);
    il.Emit(OpCodes.Ret);
}
private static TypeBuilder GetTypeBuilder(string typeName, Type parentType = null)
{
    var typeSignature = typeName;// "DynamicType";
    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);
    if(parentType != null)
        tb.SetParent(parentType);
    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);
}

我这样调用代码:

Type t = CompileType("typeName", "typeHeaderString");
object obj = CreateObject(t, parameterList);//parameterList is an array of strings

我在CreateConstructor函数的倒数第二行中收到ArgumentNullException。创建运行时构造函数时我做错了什么?我会用这种错误的方式吗?

编辑:现在,我将为每种数据类型手动创建类,尽管在接收数据时比以前创建类要好得多。

0 个答案:

没有答案