在运行时在 C# 中填充/更新枚举值

本文关键字:更新 枚举 填充 运行时 | 更新日期: 2023-09-27 18:33:00

我有Windows应用程序,我需要在运行时通过读取名为"控件.txt"的文本文件来填充枚举值。作为限制,我不应该使用字典。下面是枚举 MyControls 中可用的默认值。我只需要使用枚举

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

如果控件.txt文件可用,则枚举的内容应更改,例如

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

我如何实现这一点。我还遇到了链接 在运行时创建/修改枚举 但无法理解。

在运行时在 C# 中填充/更新枚举值

我强烈认为您正在尝试解决错误的问题。枚举的值是类型安全。我不认为动态填充它是一个好主意。真正有用的是,甚至在编译之前就有一个由文本文件(例如(填充的枚举。您可以使用 VS 中的文本模板执行此操作。

你可以在我的博客文章中找到一个例子: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

虽然我的示例从数据库加载,但将其更改为从文本文件加载应该是微不足道的。

除了我同意另一个答案说你失去类型和编译时间安全之外,使用EnumBuilderClass应该是唯一的方法(感谢huMpty duMpty的评论(。

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

输出:

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

好吧,我同意上面的用例不是我会使用的。但是,当谈到它没有用时,我不同意。例如,我们使用枚举对机器学习模块的字符串值进行分类。我们在运行时编写代码以在运行时使用它,对枚举进行分组比对字符串进行分组和分析要快得多。使用大质量的字符串时没有什么好处。在进行比较、内存分配、垃圾收集、分组、排序时,它们存在问题,字节太多了。

管理大量数据的数据库将生成字符串的哈希并存储该哈希,然后在同一语句中比较字符串哈希(不唯一,而是数字(和字符串,使 TSQL 语言在哈希字段上使用更明确的索引来缩小搜索范围,然后比较字符串值以确保使用正确的值。 在 TSQL 中,人们会这样做:

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

但在 .NET 中,我们一直认为比较字符串是要走的路。

对我来说,在这里转储我的代码是没有意义的,因为它是专有的,但是那里有很多很好的示例,Bob Dain的一篇文章逐行展示了如何做到这一点,并位于此处

他的解决方案片段如下所示:

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

可以将System.Reflection.Emit命名空间用于许多事情,可以生成一个类来为一个人制作许可证密钥。人们也可以编写代码,代码编写和更新代码是未来。