简体   繁体   English

如何在ASP.NET中使用C#创建动态类

[英]how to create dynamic class using c# in asp.net

Is it possible to do the following in C# ? 是否可以在C#中执行以下操作? I am fetching data from a database. 我正在从数据库中获取数据。 At run time I can compute the number of columns and data types of the columns fetched. 在运行时,我可以计算获取的列数和数据类型。 Next I want to "generate" a class with these data types as fields. 接下来,我想使用这些数据类型作为字段“生成”一个类。 I also want to store all the records that I fetched in a collection. 我还想存储我在集合中获取的所有记录。 The problem is that I want to do both step 1 and 2 at runtime Is this possible? 问题是我想在运行时同时执行步骤1和2。这可能吗? I am using C# currently but I can shift to something else if i need to. 我目前正在使用C#,但如果需要的话,可以改用其他软件。

Yes it is possible. 对的,这是可能的。 Take a look at System.Reflection.Emit namespace. 看一下System.Reflection.Emit命名空间。 Here are the working examples of dynamic type creation. 是动态类型创建的工作示例。 The code at the end of this answer is taken from the last link. 该答案末尾的代码取自最后一个链接。

However, it is a very small set of issues, that can be solved by dynamic creation of class. 但是,这是一堆非常小的问题,可以通过动态创建类来解决。 It is very slow and for your task it is a great example of bad smell code. 它非常慢,对于您的任务,这是不良气味代码的一个很好的例子。

If you are working with relational database, I guess, you are already know the required properties for classes, so declare it yourself or work with DataTable - it is a good practice. 如果您正在使用关系数据库,我想您已经知道类的必需属性,因此请自行声明它或使用DataTable这是一个好习惯。

If you are working with non-relational database, and/or does not want to declare classes it is better to work with Dictionary<string, object> or Hashtable instead of dynamic class. 如果您使用的是非关系型数据库,并且/或者不想声明类,那么最好使用Dictionary<string, object>Hashtable代替动态类。

If previous solutions does not fit, take a look at anonymous types . 如果以前的解决方案不适合,请查看匿名类型 It is kindly better than dynamic creation of classes. 它比动态创建类更好。

If you does not trust any of previous solutions, the worst way is to use dynamic keyword or ExpandoObject and setting propeties manually, and it is still better. 如果您不信任任何先前的解决方案,那么最糟糕的方法是使用dynamic关键字或ExpandoObject并手动设置属性,这样更好。

Dynamic declaration of class it is hardcore for that, who want to create dynamic assemblies and generate code on IL. 动态类声明对于那些想要创建动态程序集并在IL上生成代码的人来说是很难的。 It is really hardcore. 它真的是铁杆。 If I did not change your mind, so below is the code for class creation (copypasted from here ). 如果我没有改变主意,那么下面是用于创建类的代码(从此处复制)。

/*
public class MyDynamicType
{
    private int m_number;

    public MyDynamicType() : this(42) {}
    public MyDynamicType(int initNumber)
    {
        m_number = initNumber;
    }

    public int Number
    {
        get { return m_number; }
        set { m_number = value; }
    }

    public int MyMethod(int multiplier)
    {
        return m_number * multiplier;
    }
}
*/

AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder ab = 
    AppDomain.CurrentDomain.DefineDynamicAssembly(
        aName, 
        AssemblyBuilderAccess.RunAndSave);

// For a single-module assembly, the module name is usually
// the assembly name plus an extension.
ModuleBuilder mb = 
    ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

TypeBuilder tb = mb.DefineType(
    "MyDynamicType", 
     TypeAttributes.Public);

// Add a private field of type int (Int32).
FieldBuilder fbNumber = tb.DefineField(
    "m_number", 
    typeof(int), 
    FieldAttributes.Private);

// Define a constructor that takes an integer argument and 
// stores it in the private field. 
Type[] parameterTypes = { typeof(int) };
ConstructorBuilder ctor1 = tb.DefineConstructor(
    MethodAttributes.Public, 
    CallingConventions.Standard, 
    parameterTypes);

ILGenerator ctor1IL = ctor1.GetILGenerator();
// For a constructor, argument zero is a reference to the new
// instance. Push it on the stack before calling the base
// class constructor. Specify the default constructor of the 
// base class (System.Object) by passing an empty array of 
// types (Type.EmptyTypes) to GetConstructor.
ctor1IL.Emit(OpCodes.Ldarg_0);
ctor1IL.Emit(OpCodes.Call, 
    typeof(object).GetConstructor(Type.EmptyTypes));
// Push the instance on the stack before pushing the argument
// that is to be assigned to the private field m_number.
ctor1IL.Emit(OpCodes.Ldarg_0);
ctor1IL.Emit(OpCodes.Ldarg_1);
ctor1IL.Emit(OpCodes.Stfld, fbNumber);
ctor1IL.Emit(OpCodes.Ret);

// Define a default constructor that supplies a default value
// for the private field. For parameter types, pass the empty
// array of types or pass null.
ConstructorBuilder ctor0 = tb.DefineConstructor(
    MethodAttributes.Public, 
    CallingConventions.Standard, 
    Type.EmptyTypes);

ILGenerator ctor0IL = ctor0.GetILGenerator();
// For a constructor, argument zero is a reference to the new
// instance. Push it on the stack before pushing the default
// value on the stack, then call constructor ctor1.
ctor0IL.Emit(OpCodes.Ldarg_0);
ctor0IL.Emit(OpCodes.Ldc_I4_S, 42);
ctor0IL.Emit(OpCodes.Call, ctor1);
ctor0IL.Emit(OpCodes.Ret);

// Define a property named Number that gets and sets the private 
// field.
//
// The last argument of DefineProperty is null, because the
// property has no parameters. (If you don't specify null, you must
// specify an array of Type objects. For a parameterless property,
// use the built-in array with no elements: Type.EmptyTypes)
PropertyBuilder pbNumber = tb.DefineProperty(
    "Number", 
    PropertyAttributes.HasDefault, 
    typeof(int), 
    null);

// The property "set" and property "get" methods require a special
// set of attributes.
MethodAttributes getSetAttr = MethodAttributes.Public | 
    MethodAttributes.SpecialName | MethodAttributes.HideBySig;

// Define the "get" accessor method for Number. The method returns
// an integer and has no arguments. (Note that null could be 
// used instead of Types.EmptyTypes)
MethodBuilder mbNumberGetAccessor = tb.DefineMethod(
    "get_Number", 
    getSetAttr, 
    typeof(int), 
    Type.EmptyTypes);

ILGenerator numberGetIL = mbNumberGetAccessor.GetILGenerator();
// For an instance property, argument zero is the instance. Load the 
// instance, then load the private field and return, leaving the
// field value on the stack.
numberGetIL.Emit(OpCodes.Ldarg_0);
numberGetIL.Emit(OpCodes.Ldfld, fbNumber);
numberGetIL.Emit(OpCodes.Ret);

// Define the "set" accessor method for Number, which has no return
// type and takes one argument of type int (Int32).
MethodBuilder mbNumberSetAccessor = tb.DefineMethod(
    "set_Number", 
    getSetAttr, 
    null, 
    new Type[] { typeof(int) });

ILGenerator numberSetIL = mbNumberSetAccessor.GetILGenerator();
// Load the instance and then the numeric argument, then store the
// argument in the field.
numberSetIL.Emit(OpCodes.Ldarg_0);
numberSetIL.Emit(OpCodes.Ldarg_1);
numberSetIL.Emit(OpCodes.Stfld, fbNumber);
numberSetIL.Emit(OpCodes.Ret);

// Last, map the "get" and "set" accessor methods to the 
// PropertyBuilder. The property is now complete. 
pbNumber.SetGetMethod(mbNumberGetAccessor);
pbNumber.SetSetMethod(mbNumberSetAccessor);

// Define a method that accepts an integer argument and returns
// the product of that integer and the private field m_number. This
// time, the array of parameter types is created on the fly.
MethodBuilder meth = tb.DefineMethod(
    "MyMethod", 
    MethodAttributes.Public, 
    typeof(int), 
    new Type[] { typeof(int) });

ILGenerator methIL = meth.GetILGenerator();
// To retrieve the private instance field, load the instance it
// belongs to (argument zero). After loading the field, load the 
// argument one and then multiply. Return from the method with 
// the return value (the product of the two numbers) on the 
// execution stack.
methIL.Emit(OpCodes.Ldarg_0);
methIL.Emit(OpCodes.Ldfld, fbNumber);
methIL.Emit(OpCodes.Ldarg_1);
methIL.Emit(OpCodes.Mul);
methIL.Emit(OpCodes.Ret);

// Finish the type.
Type t = tb.CreateType();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM