【问题标题】:Making public the internal field "value__" of dynamic enum from C# .NET 2.0从 C# .NET 2.0 公开动态枚举的内部字段“value__”
【发布时间】:2014-11-07 09:40:06
【问题描述】:

在 Unity 中,我创建了一个动态枚举并成功将其保存到生成的 .dll 中,如下所示:msdn article 和此处:Dynamic enum in C# 我刚刚在动态创建的枚举上添加了 FlagsAttribute

但我注意到内部字段“value__”在 .NET 2.0 上是私有的。因此,当我在另一个 c# 应用程序中使用此 dll 时,我无法将枚举值直接转换为 int 或获取确切的掩码值。请注意,在 .NET 4.0 及更高版本中,此“value__”字段是公共的。另请注意,如果您在 C# 中创建 2.0 dll 中的经典公共枚举,则此“value__”也是公共的(在 dll 中)。

所以我的问题是如何使用 EnumBuilder 在 Mono .NET 2.0 中公开这个特殊字段?

这是我生成枚举的代码:

    static bool CreateEnumerators(AppDomain currentDomain, AssemblyName asmName, string enumName, string relativePathName = "")
    {
        string targetPathName = currentDomain.BaseDirectory + relativePathName;

        if (!System.IO.Directory.Exists(targetPathName))
            System.IO.Directory.CreateDirectory(targetPathName);

        // Create a dynamic assembly in the current application domain,
        // and allow it to be executed and saved to disk.
        AssemblyBuilder asmBuilder = currentDomain.DefineDynamicAssembly(asmName,
                                                                              AssemblyBuilderAccess.RunAndSave,
                                                                              targetPathName);

        // Define a dynamic module
        // For a single-module assembly, the module has the same name as the assembly.
        ModuleBuilder mdlBuilder = asmBuilder.DefineDynamicModule(asmName.Name,
                                                                          asmName.Name + ".dll");

        // Define a public enumeration and an underlying type of Integer.
        EnumBuilder enumBuilder = mdlBuilder.DefineEnum(asmName.Name + "." + enumName,
                                                      TypeAttributes.Public, typeof(int));

        // Get data from database
        int key = 1;
        foreach (string literalName in enumLiteralNames)
        {
            enumBuilder.DefineLiteral(literalName, key);
            key = key << 1;
        }

        // set FlagsAttribute
        ConstructorInfo cinfo = typeof(FlagsAttribute).GetConstructor(Type.EmptyTypes);
        CustomAttributeBuilder flagsAttribute = new CustomAttributeBuilder(cinfo, new object[] { });
        enumBuilder.SetCustomAttribute(flagsAttribute);

        // Create the enum
        Type finished = enumBuilder.CreateType();

        // Check if "value__" is private (by default it is from .NET 2.0 to 3.5)
        Console.WriteLine("\nCheck special field: \n");
        {
            FieldInfo fi = finished.GetField("value__", BindingFlags.Instance | BindingFlags.NonPublic);
            if (fi != null)
                Console.WriteLine("found as private: " + finished.Name + "." + fi.Name + "{" + fi.Attributes + "}");
        }

        // in .NET 4.0 and above "value__" is part of the public fields
        Console.WriteLine("Fields:");
        foreach (FieldInfo fi in finished.GetFields())
        {

            Console.WriteLine(finished.Name + "." + fi.Name + " " + fi.GetType() + " ");

        }

        // Finally, save the assembly
        string assemblyName = asmName.Name + ".dll";
        asmBuilder.Save(assemblyName);
        Console.WriteLine("\nCreated assembly '" + targetPathName + assemblyName + "'");

        return true;
    }

这里只是一个简单的使用产生错误:

        MyTypes.MEnum eTypePhysical = MyTypes.MEnum.Physical;
        Debug.Log("Value =" + (int)eTypePhysical);

错误:

Internal compiler error. See the console log for more information. output was:error CS0656: The compiler required member `MyTypes.MyEnum.value__' could not be found or is inaccessible
error CS0656: The compiler required member `MyTypes.MyEnum.value__' could not be found or is inaccessible

对枚举内部值的任何访问都会产生相同的错误。

我在 Visual Studio 上使用 Microsft .NET 2.0 框架生成 dll 并使用它时没有出现任何错误。但仍然通过检查 dll,我看到动态枚举的“value__”是私有的,这显然是导致 Unity 错误的原因。这就是为什么我想知道是否可以使用 .NET 2.0 的 EnumBuilder 接口将其公开

【问题讨论】:

  • 您能否发布一些示例代码,这些示例代码利用了该字段并且不能在您的情况下使用,因为它是私有的?我不明白你想要达到什么目的。
  • 我试图在正文中澄清最初的帖子
  • 动态枚举在术语上是矛盾的。您要么有一个静态的值列表,要么没有。这就像说 - 我正在创建一个动态常量......
  • 这取决于上下文。它在应用程序运行时是静态的,但在编辑器时是动态的。灵活性适用于生产过程中的设计师。当应用程序发布时,不再需要这种灵活性,这就是为什么数据包含在该枚举中(和性能问题)。我们可以更长时间地讨论这个目标,但我真的更愿意回答这个问题的技术点。

标签: c# .net dynamic reflection enums


【解决方案1】:

我在 .NET 2.0 上使用 System.Reflection 尝试了很多不同的方法来解决此问题,但均未成功。即:我尝试将 internalVisibleToFlags 添加到程序集,以使用 TypeBuilder 从头开始​​创建枚举类型。但是它们都不起作用,对于后一种情况,当您使用 Net framework 2.0 动态构建类型时,显然有一些限制阻止使用 System.Enum 的继承 我终于切换到为 .NET 2.0 目标编译的 c# lib Mono.Cecil。 下面是使用 Mono.Cecil 使用公共特殊字段“value__”构建枚举的代码。

static void CreateEnumAssembly(string asmName, string relativePathName = "")
{
    // get asm version from database
    Version asmVersion = new Version(0,0,0,0);
    AssemblyNameDefinition asmNameDef = new AssemblyNameDefinition(asmName, asmVersion);
    AssemblyDefinition asmDef = AssemblyDefinition.CreateAssembly (asmNameDef, asmName, ModuleKind.Dll);

    // get enum name from database
    string enumName = "myEnum";

    // define a new enum type
    TypeDefinition enumTypeDef;
    asmDef.MainModule.Types.Add (enumTypeDef = new TypeDefinition ( asmName, enumName, TypeAttributes.Public | TypeAttributes.Sealed, asmDef.MainModule.Import(typeof(System.Enum) )));
    // - add FlagsAttribute to the enum
    CustomAttribute flagsAttribute = new CustomAttribute ( asmDef.MainModule.Import(typeof(FlagsAttribute).GetConstructor(Type.EmptyTypes)) );
    enumTypeDef.CustomAttributes.Add( flagsAttribute );

    // define the special field "value__" of the enum
    enumTypeDef.Fields.Add (new FieldDefinition ("value__", FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName, asmDef.MainModule.Import (typeof(System.Int32))));

    int shift = 0;
    // get literals and their values from database and define them in the enum
    foreach (var literalName in literalNames) 
    {
        FieldDefinition literalDef;
        enumTypeDef.Fields.Add (literalDef = new FieldDefinition (literalName, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal | FieldAttributes.HasDefault, enumTypeDef));
        System.Int32 key = 1 << shift++;
        literalDef.Constant = key;
    }

    string filename = relativePathName+asmNameDef.Name+".dll";
    asmDef.Write(filename);
    Debug.Log ("Created assembly '"+filename+"'");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多