c#使用Reflection.Emit将定义好的类输出到动态模块

本文关键字:输出 动态 模块 Reflection 使用 Emit 定义 | 更新日期: 2023-09-27 18:11:27

Microsoft在这里展示了如何创建一个动态类:

http://msdn.microsoft.com/en-us/library/system.reflection.emit.modulebuilder (v = vs.71) . aspx

定义了一个自定义对象,其中定义了一个构造函数和一个方法。我有一个类定义,有没有办法发出类我已经写了,而不是试图写它的例子显示?

谢谢FacticiusVir,它几乎完成了。然而,它似乎并没有完全到位,"国家。"语言"

"不支持USA

完整代码,包括FacticiusVir的答案:

class DynamicEnums
{
    public static void Main()
    {
        AppDomain domain = AppDomain.CurrentDomain;
        AssemblyName aName = new AssemblyName("DynamicEnums");
        AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save);
        ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
        ConstructorInfo referenceObjectConstructor = typeof(ReferenceObject).GetConstructor(new[] { typeof(int) });
        List<Type> types = new List<Type>();
        foreach(ReferenceType rt in GetTypes())
        {
            TypeBuilder tb = mb.DefineType(rt.Name, TypeAttributes.Public);
            ConstructorBuilder staticConstructorBuilder = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
            ILGenerator staticConstructorILGenerator = staticConstructorBuilder.GetILGenerator();
            foreach (Reference r in GetReferences(rt.ID))
            {
                string name;
                if (rt.Name == "Countries")
                    name = r.Abbreviation.Trim();
                else if (rt.Name == "PermanentFundDividends")
                    name = "Year" + r.Abbreviation.Trim();
                else
                    name = NameFix(r.Name);
                // Create a public, static, readonly field to store the
                // named ReferenceObject.
                FieldBuilder referenceObjectField = tb.DefineField(name, typeof(ReferenceObject), FieldAttributes.Static | FieldAttributes.Public | FieldAttributes.InitOnly);
                // Add code to the static constructor to populate the
                // ReferenceObject field:
                // Load the ReferenceObject's ID value onto the stack as a
                // literal 4-byte integer (Int32).
                staticConstructorILGenerator.Emit(OpCodes.Ldc_I4, r.ID);
                // Create a reference to a new ReferenceObject on the stack
                // by calling the ReferenceObject(int32 pValue) reference
                // we created earlier.
                staticConstructorILGenerator.Emit(OpCodes.Newobj, referenceObjectConstructor);
                // Store the ReferenceObject reference to the static
                // ReferenceObject field.
                staticConstructorILGenerator.Emit(OpCodes.Stsfld, referenceObjectField);
            }
            staticConstructorILGenerator.Emit(OpCodes.Ret);
            types.Add(tb.CreateType());
        }
        try
        {
            ab.Save(aName.Name + ".dll");
        }
        catch (Exception)
        {
            Console.WriteLine("Could not save .dll, file must already be loaded.");
        }
        foreach (Type t in types)
        {
            foreach (FieldInfo o in t.GetFields())
            {
                Console.WriteLine("{0}.{1} = {2}", t, o.Name, "Later");  //Don't know how to get Value doing it this way
            }
            Console.WriteLine();
            //Console.ReadKey();
        }
        Console.WriteLine();
        Console.WriteLine("Dynamic Enums Built Successfully.");
        //Console.ReadKey();
    }
    public static List<ReferenceType> GetTypes()
    {
        List<ReferenceType> referenceTypes = new List<ReferenceType>();
        referenceTypes.Add(new ReferenceType { ID = 1, Name = "Countries" });
        return referenceTypes;
    }
    public static List<Reference> GetReferences(int typeID)
    {
        List<Reference> references = new List<Reference>();
        references.Add(new Reference { ID = 120, Abbreviation = "USA" });
        return references;
    }
    public struct ReferenceType
    {
        public int ID;
        public string Name;
    }
    public struct Reference
    {
        public int ID;
        public int TypeID;
        public string Abbreviation;
        public string Name;
    }
    public static string NameFix(string name)
    {
        //Strip all non alphanumeric characters
        string r = Regex.Replace(name, @"[^'w]", "");
        //Enums cannot begin with a number
        if (Regex.IsMatch(r, @"^'d"))
            r = "N" + r;
        return r;
    }
}
public class ReferenceObject
{
    private readonly int value;
    public ReferenceObject(int pValue)
    {
        value = pValue;
    }
    public override string ToString()
    {
        return value.ToString();
    }
    public int Value()
    {
        return value;
    }
    public int ID()
    {
        return value;
    }
    #region == Operator
    public static bool operator ==(int objLeft, ReferenceObject objRight)
    {
        return objLeft == objRight.value;
    }
    public static bool operator ==(ReferenceObject objLeft, int objRight)
    {
        return objLeft.value == objRight;
    }
    public static bool operator ==(string objLeft, ReferenceObject objRight)
    {
        return objLeft == objRight.value.ToString();
    }
    public static bool operator ==(ReferenceObject objLeft, string objRight)
    {
        return objLeft.value.ToString() == objRight;
    }
    #endregion
    #region != Operator
    public static bool operator !=(int objLeft, ReferenceObject objRight)
    {
        return objLeft != objRight.value;
    }
    public static bool operator !=(ReferenceObject objLeft, int objRight)
    {
        return objLeft.value != objRight;
    }
    public static bool operator !=(string objLeft, ReferenceObject objRight)
    {
        return objLeft != objRight.value.ToString();
    }
    public static bool operator !=(ReferenceObject objLeft, string objRight)
    {
        return objLeft.value.ToString() != objRight;
    }
    #endregion
    public override bool Equals(object obj)
    {
        if ((obj is ReferenceObject))
            return value == ((ReferenceObject)obj).value;
        if ((obj is int))
            return value == (int)obj;
        if ((obj is string))
            return value.ToString() == (string)obj;
        return false;
    }
    public override int GetHashCode()
    {
        return value;
    }
}

c#使用Reflection.Emit将定义好的类输出到动态模块

好的,我已经假设了Reference &参考类型看起来像这样:

public class ReferenceType
{
    public string Name { get; set; }
    public int ID { get; set; }
}
public class Reference
{
    public string Abbreviation { get; set; }
    public int ID { get; set; }
}

,你想要生成的类看起来像这样:

public static class Countries
{
    public static readonly ReferenceObject USA = new ReferenceObject(120);
    public static readonly ReferenceObject CAN = new ReferenceObject(13);
    //...
}

您需要做的是创建一组字段(我将这些字段设置为静态和只读的,如果您试图模仿枚举,这是一个很好的实践),然后从静态构造函数填充它们,例如:

AppDomain domain = AppDomain.CurrentDomain;
AssemblyName aName = new AssemblyName("DynamicEnums");
AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
// Store a handle to the ReferenceObject(int32 pValue)
// constructor.
ConstructorInfo referenceObjectConstructor = typeof(ReferenceObject).GetConstructor(new[] { typeof(int) });
foreach (ReferenceType rt in GetTypes())
{
    TypeBuilder tb = mb.DefineType(rt.Name, TypeAttributes.Public);
    // Define a static constructor to populate the ReferenceObject
    // fields.
    ConstructorBuilder staticConstructorBuilder = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
    ILGenerator staticConstructorILGenerator = staticConstructorBuilder.GetILGenerator();
    foreach (Reference r in GetReferences(rt.ID))
    {
        string name = r.Abbreviation.Trim();
        // Create a public, static, readonly field to store the
        // named ReferenceObject.
        FieldBuilder referenceObjectField = tb.DefineField(name, typeof(ReferenceObject), FieldAttributes.Static | FieldAttributes.Public | FieldAttributes.InitOnly);
        // Add code to the static constructor to populate the
        // ReferenceObject field:
        // Load the ReferenceObject's ID value onto the stack as a
        // literal 4-byte integer (Int32).
        staticConstructorILGenerator.Emit(OpCodes.Ldc_I4, r.ID);
        // Create a reference to a new ReferenceObject on the stack
        // by calling the ReferenceObject(int32 pValue) reference
        // we created earlier.
        staticConstructorILGenerator.Emit(OpCodes.Newobj, referenceObjectConstructor);
        // Store the ReferenceObject reference to the static
        // ReferenceObject field.
        staticConstructorILGenerator.Emit(OpCodes.Stsfld, referenceObjectField);
    }
    // Finish the static constructor.
    staticConstructorILGenerator.Emit(OpCodes.Ret);
    tb.CreateType();
}
ab.Save(aName.Name + ".dll");

----编辑----

要访问生成的DLL中字段的值,您有几个选项。第一种方法是运行这段代码,获取它生成的"Dynamic Enums.dll"文件的副本,并直接从包含运行时代码的任何其他项目中引用;例如,你有一个在构建时执行的项目来生成DLL(如上所述),另一个单独的项目引用DLL并执行应用程序的运行时工作。这样做的好处是您可以直接在代码中引用生成的类(例如SomeMethod(Countries.USA)if(someVariable == Countries.CAN)),而缺点是您必须在构建过程中使用上述代码,或者在源数据库更改时记得重新生成dll。如果这就是你正在寻找的,我建议你看看专门的代码生成工具,比如T4,它是内置于Visual Studio中的。

上面的选项似乎是直接访问您生成的动态程序集,而它仍然保存在内存中。为此,必须将程序集标记为可运行和可保存:

AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave);

事实上,你可以把它标记为AssemblyBuilderAccess.Run,但我假设你仍然想保存输出。

然后可以使用FieldInfo。GetValue(object obj)方法获取静态值:

    foreach (Type t in types)
    {
        foreach (FieldInfo o in t.GetFields())
        {
            // As this is a static field no instance of type 't' is
            // required to get the field value, so just pass null
            ReferenceObject value = o.GetValue(null) as ReferenceObject;
            Console.WriteLine("{0}.{1} = {2}", t, o.Name, value);
        }
        Console.WriteLine();
    }