简体   繁体   中英

Fill/update the enum values at runtime in C#

I have windows app where-in i need to fill enum values at runtime by reading a text file named "Controls.txt". As restriction, i'm not suppose to use dictionary . Below is the default values available in the enum MyControls. I have to use enums only .

public enum MyControls
{
   Button1 = 0,
   Button2 = 1,
   Button3 = 2,
}

If Controls.txt file is available, then content of enum should change like

public enum MyControls
{
   btn1 = 0,
   btn2 = 1,
   btn3 = 2,
}

how do i achieve this. I also came across the link Creating / Modifying Enums at Runtime but could not get idea.

I strongly think you are trying to solve the wrong problem. The value of enum is type-safety. I do not think that filling it up dynamically is a good idea. What would really be useful is to have an enum populated by a text file (for example) even before compilation. You can do this using text templates in VS.

You can find an example in my blog post here: http://skleanthous.azurewebsites.net/post/2014/05/21/Creating-enums-from-the-database-and-using-them-in-Entity-framework-5-and-later-in-model-first

Although my example loads from a db, changing it to load from a text file should be trivial.

Apart from the fact that i agree with the other answer that says that you lose type and compile time safety, using EnumBuilderClass should be the only way (thanks to huMpty duMpty 's comment).

// sample "file":
string fileContent = @"
btn1 = 0,
btn2 = 1,
btn3 = 2,
";
var enumBody = fileContent.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
    .Select(line => new { bothToken = line.Trim().Trim(',').Split('=') })
    .Where(x => x.bothToken.Length == 2)
    .Select(x => new { Name = x.bothToken[0].Trim(), Value = int.Parse(x.bothToken[1].Trim()) });

AppDomain currentDomain = AppDomain.CurrentDomain;
AssemblyName asmName = new AssemblyName("EnumAssembly");
AssemblyBuilder asmBuilder = currentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = asmBuilder.DefineDynamicModule(asmName.Name, asmName.Name + ".dll");
string enumTypeName = string.Format("{0}.{1}", typeof(MyControls).Namespace, typeof(MyControls).Name);
EnumBuilder eb = mb.DefineEnum(enumTypeName, TypeAttributes.Public, typeof(int));
foreach(var element in enumBody)
{
    FieldBuilder fb1 = eb.DefineLiteral(element.Name, element.Value);
}
Type eType = eb.CreateType();

foreach (object obj in Enum.GetValues(eType))
{
    Console.WriteLine("{0}.{1} = {2}", eType, obj, ((int)obj));
}

Output:

Namespacename.MyControls.btn1 = 0
Namespacename.MyControls.btn2 = 1
Namespacename.MyControls.btn3 = 2

Well, I agree that the use case above is not something I would use. I, however, do not agree when it comes to there being no use for it. We use for example use enums to classify string values for machine learning modules. We write code at runtime to use it at runtime and grouping enums is a hell of a lot faster than grouping and analysing strings. There is nothing good when using strings in large qualities. They are problematic when doing a comparison, memory allocation, garbage collections, grouping, sorting, there are just too many bytes.

Databases that manage large volumes of data will generate a hash of a string and store that, then compare the strings hash (not unique but a number) and the string at the same statement making the TSQL language use the more definitive index on the hash field to narrow the search, then comparing the string values to make sure the right value is used. in TSQL one would do it this way:

SELECT *   
FROM Production.Product  
WHERE CHECKSUM(N'Bearing Ball') = cs_Pname  
AND Name = N'Bearing Ball';  
GO 

but in .net we keep thinking that comparing strings is the way to go.

It makes little sense for me to dump my code here as it is proprietary but that there is plenty of good samples out there, an Article by Bob Dain shows line by line how this can be done and is located here

A snippet of his solution looks like this:

using System;
using System.Reflection;
using System.IO;

namespace RemoteUser
{
    public class RemoteUserClass
    {
        public RemoteUserClass()
        {
            // Load the remote assembly
            AssemblyName name = new AssemblyName();
            name.CodeBase = "file://" + Directory.GetCurrentDirectory() + 
                            "ThirdPartyDll.dll";
            Assembly assembly = AppDomain.CurrentDomain.Load(name);

            // Instantiate the class
            object remoteObject = 
              assembly.CreateInstance("ThirdPartyDll.ThirdPartyClass");
            Type remoteType = 
              assembly.GetType("ThirdPartyDll.ThirdPartyClass");

            // Load the enum type
            PropertyInfo flagsInfo = 
              remoteType.GetProperty("ThirdPartyBitFields");
            Type enumType = assembly.GetType("ThirdPartyDll.BitFields");

            // Load the enum values
            FieldInfo enumItem1 = enumType.GetField("AnotherSetting");
            FieldInfo enumItem2 = enumType.GetField("SomethingElse");

            // Calculate the new value
            int enumValue1 = (int)enumItem1.GetValue(enumType);
            int enumValue2 = (int)enumItem2.GetValue(enumType);
            int currentValue = (int)flagsInfo.GetValue(remoteObject, null);
            int newValue = currentValue | enumValue1 | enumValue2;

            // Store the new value back in Options.FieldFlags
            object newEnumValue = Enum.ToObject(enumType, newValue);
            flagsInfo.SetValue(remoteObject, newEnumValue, null);

            // Call the method
            MethodInfo method = remoteType.GetMethod("DoSomeGood");
            method.Invoke(remoteObject, null);
        }
    }
}

One can use the System.Reflection.Emit namespace for many things, one can generate a class that makes a license key for one. One can also write code, and code writing and updating code is the future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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