【问题标题】:Fill/update the enum values at runtime in C#在 C# 运行时填充/更新枚举值
【发布时间】:2014-12-04 09:05:48
【问题描述】:

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

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

如果 Controls.txt 文件可用,那么枚举的内容应该改变

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

我如何做到这一点。我也看到了链接Creating / Modifying Enums at Runtime,但不明白。

【问题讨论】:

  • 你看过EnumBuilder Class吗?您也可能需要查看Dynamic enum in C#
  • 为什么首先使用enum
  • 这里有什么要求?它是用于自动将 Enum 绑定到组合框的框架,还是类似的东西?
  • 为什么你必须使用枚举而不是字典?
  • @huMptyduMpty:你应该把它写成答案;它肯定回答了这个问题并使用了反射发射命名空间:) 我会投赞成票。

标签: c# dynamic enums


【解决方案1】:

我强烈认为您正在尝试解决错误的问题。 enum 的值是类型安全的。我认为动态填充它不是一个好主意。真正有用的是让一个枚举由一个文本文件(例如)填充,即使是 before 编译。你可以在 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

虽然我的示例从数据库加载,但将其更改为从文本文件加载应该很简单。

【讨论】:

    【解决方案2】:

    除了我同意另一个答案说你失去类型和编译时安全之外,使用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
    

    【讨论】:

      【解决方案3】:

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

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

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

      但在 .net 中,我们一直认为比较字符串是可行的方法。

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

      他的解决方案的 sn-p 如下所示:

      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 命名空间做很多事情,一个人可以生成一个类来为一个人制作许可证密钥。也可以写代码,写代码和更新代码才是未来。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-05-05
        • 1970-01-01
        • 2021-05-31
        • 1970-01-01
        • 2018-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多