1

In Unity, I create a dynamic enum and I save it into a generated .dll successfully, as shown here: msdn article and here: Dynamic enum in C# I've just added the FlagsAttribute on the dyanmically created enum

But I noticed that the internal field "value__" is private on .NET 2.0. So when I use this dll in another c# application I cannot cast directly enum values to int or getting the exact masked values. Note that in .NET 4.0 and above this "value__" field is public. Note also that if you create in C# a classic public enum in a 2.0 dll this "value__" is public also (in the dll).

So my question is how to make this special field public in Mono .NET 2.0 using the EnumBuilder ?

Here my code to generate the enum:

    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;
    }

Here just a simple use producing error:

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

The error:

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

Any access to the internal values of the enum produce the same error.

I couldn't get any error on Visual Studio using the Microsft .NET 2.0 Framework for generating the dll and use it. But still by inspecting the dll I see the "value__" of the dynamic enum as private which is apparently what causes that error in Unity. This is why I would like to know if it's possible to declare it public using the EnumBuilder interface for .NET 2.0

Community
  • 1
  • 1
  • Can you post some sample code which takes advantage of this field and can't be used in your case, because it is private? i don't understand what you're trying to achieve. – Damir Arh Sep 13 '14 at 08:55
  • I tried to clarify the initial post in its body – xldeveloper Sep 13 '14 at 11:31
  • Dynamic Enum is a contradiction in terms. You either have a static list of values or you don't. It's like saying - I'm creating a dynamic constant... – Goran Sep 15 '14 at 09:05
  • It depends on the context. It's static at application run time, but dynamic at editor time. The flexibility is for designers during production process. When the application is released this flexiblity is no more needed and that's why data is contained in that enum (and performance matter). We can discuss of the goal of this longer but I would really prefer an answer to the technical point of the question. – xldeveloper Sep 17 '14 at 16:35

1 Answers1

0

I've tried a lot of different things with System.Reflection on .NET 2.0 to solve this issue without any success. ie: I tried to add internalVisibleToFlags to the assembly, to create an enum type from scratch by using a TypeBuilder. But none of them worked, for the late case there are apparently constraints that prevent use of inheritance from a System.Enum when you are building dynamically a type with Net framework 2.0 I've finally switched to c# lib Mono.Cecil compiled for .NET 2.0 target. Below is the code to build the enum with a public special field "value__" using Mono.Cecil.

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+"'");
}