Fill/update the enum values at runtime in C#
我有Windows应用程序,在其中我需要在运行时通过读取名为" Controls.txt"的文本文件来填充枚举值。
作为限制,我不打算使用字典。 以下是枚举MyControls中可用的默认值。 我只需要使用枚举。
1 2 3 4 5 6 | public enum MyControls { Button1 = 0, Button2 = 1, Button3 = 2, } |
如果Controls.txt文件可用,则枚举的内容应更改为
1 2 3 4 5 6 | public enum MyControls { btn1 = 0, btn2 = 1, btn3 = 2, } |
我该如何做到这一点。 我还遇到了在运行时创建/修改枚举的链接,但不清楚。
我强烈认为您正在尝试解决错误的问题。枚举的值是类型安全的。我认为动态填充它不是一个好主意。真正有用的是甚至在编译之前也要用文本文件(例如)填充一个枚举。您可以使用VS中的文本模板来执行此操作。
您可以在我的博客文章中找到一个示例,网址为:http://skleanthous.azurewebsites.net/post/2014/05/21/Creating-enums-from-the-database-and-using-them-in-Entity-framework -5-及以后的模型优先
尽管我的示例从数据库加载,但将其更改为从文本文件加载应该是微不足道的。
除了我同意另一个回答,即您丢失类型和编译时间安全性这一事实外,使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // 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)); } |
输出:
1 2 3 | Namespacename.MyControls.btn1 = 0 Namespacename.MyControls.btn2 = 1 Namespacename.MyControls.btn3 = 2 |
好吧,我同意上面的用例不是我要使用的东西。但是,对于没有用处,我不同意。例如,我们使用枚举对机器学习模块的字符串值进行分类。我们在运行时编写代码以在运行时使用它,并且对枚举进行分组比对字符串进行分组和分析要快得多。当使用大质量的字符串时,没有什么好处。当进行比较,内存分配,垃圾回收,分组,排序时,它们有问题,因为字节太多。
管理大量数据的数据库将生成字符串的哈希并进行存储,然后在同一条语句中比较字符串哈希(不是唯一的,而是数字)和字符串,从而使TSQL语言在哈希字段上使用更确定的索引缩小搜索范围,然后比较字符串值以确保使用正确的值。在TSQL中,可以这样做:
1 2 3 4 5 | SELECT * FROM Production.Product WHERE CHECKSUM(N'Bearing Ball') = cs_Pname AND Name = N'Bearing Ball'; GO |
但是在.net中,我们一直认为比较字符串是正确的方法。
对于我来说,将代码转储到这里是没有意义的,因为它是专有的,但是那里有很多好的示例,Bob Dain的文章逐行展示了如何做到这一点,并位于此处。
他的解决方案的摘要如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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命名空间用于很多事情,一个人可以生成一个为一个许可证密钥的类。一个人也可以编写代码,并且编写和更新代码是未来。