【问题标题】:Creating / Modifying Enums at Runtime在运行时创建/修改枚举
【发布时间】:2009-04-14 02:31:34
【问题描述】:

我正在创建一个程序,用户可以选择创建自己的自定义属性,这些属性最终将显示在PropertyGrid 中。现在我不想弄乱自定义编辑器,所以我只允许原始类型属性(stringintdoubleDateTimebool 等)即已经内置了编辑器。

但是,我还想为用户提供创建多项选择属性的选项,他们可以在其中定义可能值的列表,这些值又将显示为PropertyGrid 中的下拉列表。

当我在代码中硬编码Enum 时,属性网格会自动将enum 的属性显示为下拉列表。但是我可以在运行时创建和/或修改枚举,以便用户可以添加另一个属性选项,然后返回PropertyGrid 并在下拉列表中查看他们的新选项吗?

更新

考虑到帕特里克的评论,我认为Enums 在这种情况下不是正确的方法。那么,如何使用字符串列表填充PropertyGrid 项目中的下拉列表?这需要自定义编辑器吗?

【问题讨论】:

    标签: c# enums propertygrid


    【解决方案1】:

    答案在一个简单的类中:TypeConverter。 (是的,枚举不适合这里)。

    由于我没有太多细节,我假设您有一个 PropertyGrid 通过 SelectedObject 属性“链接”到目标实例,并且您的目标实例实现了 ICustomTypeDescriptor,以便您可以在运行时添加属性(即 PropertyDescriptors) .我不知道你的设计,但如果你不这样做,我建议你看看它。

    现在假设您添加了一个字符串属性,并且您想让您的用户为该属性指定一组约束。您的 UI 让用户输入一组字符串,然后您会得到一个字符串列表。也许您在目标实例中保留了一个属性字典,所以我们假设这个新列表也存储在那里。

    现在,只需编写一个从 TypeConverter 派生的新转换器(在本例中可能是 StringConverter)。您必须重写 GetStandardValuesSupported 以返回 true,并重写 GetStandardValues 以返回字符串列表(使用 context 参数访问 Instance 属性及其字符串列表)。此转换器将由您的 PropertyDescriptor 使用 PropertyDescriptor.Converter 属性发布。

    我希望这不是太模糊。如果您对此流程有任何具体问题,请告诉我。

    【讨论】:

      【解决方案2】:

      解决您的问题的典型工程解决方案是使用该列表作为数据库中的参考数据进行维护。一般来说,枚举是在编译时定义的常量,不鼓励在以后发布的代码中修改它们(更不用说运行时了),因为它可能会在 switch 语句中产生副作用。

      【讨论】:

        【解决方案3】:

        您可以使用您的代码创建代码,然后将其保存到临时文本文件中,然后使用它。这会很慢,因为它涉及使用 HDD。我建议查看reflection

        编辑:我在我的一本书中找到了一个完美的例子,在这里(它很长,但如果你将它复制到 VS 中,它会更有意义)。

        namespace Programming_CSharp
        {
           using System;
           using System.Diagnostics;
           using System.IO;
           using System.Reflection;
           using System.Reflection.Emit;
           using System.Threading;
        
           // used to benchmark the looping approach
           public class MyMath
           {
              // sum numbers with a loop
              public int DoSumLooping(int initialVal)
              {
                 int result = 0;
                 for(int i = 1;i <=initialVal;i++)
                 {
                    result += i;
                 }
                 return result;
              }
           }
        
           // declare the interface
           public interface IComputer
           {
              int ComputeSum(  );
           }
        
           public class ReflectionTest
           {
              // the private method which emits the assembly
              // using op codes
              private Assembly EmitAssembly(int theValue)
              {
                 // Create an assembly name
                 AssemblyName assemblyName = 
                    new AssemblyName(  );
                 assemblyName.Name = "DoSumAssembly";
        
                 // Create a new assembly with one module
                 AssemblyBuilder newAssembly =
                    Thread.GetDomain(  ).DefineDynamicAssembly(
                    assemblyName, AssemblyBuilderAccess.Run);
                 ModuleBuilder newModule =
                    newAssembly.DefineDynamicModule("Sum");
        
                 //  Define a public class named "BruteForceSums " 
                 //  in the assembly.
                 TypeBuilder myType =
                    newModule.DefineType(
                    "BruteForceSums", TypeAttributes.Public);
        
                 // Mark the class as implementing IComputer.
                 myType.AddInterfaceImplementation(
                    typeof(IComputer));
        
                 // Define a method on the type to call. Pass an
                 // array that defines the types of the parameters,
                 // the type of the return type, the name of the 
                 // method, and the method attributes.
                 Type[] paramTypes = new Type[0];
                 Type returnType = typeof(int);
                 MethodBuilder simpleMethod =
                    myType.DefineMethod(
                    "ComputeSum",
                    MethodAttributes.Public | 
                    MethodAttributes.Virtual,
                    returnType,
                    paramTypes);
        
                 // Get an ILGenerator. This is used
                 // to emit the IL that you want.
                 ILGenerator generator = 
                    simpleMethod.GetILGenerator(  );
        
                 // Emit the IL that you'd get if you 
                 // compiled the code example 
                 // and then ran ILDasm on the output.
        
                 // Push zero onto the stack. For each 'i' 
                 // less than 'theValue', 
                 // push 'i' onto the stack as a constant
                 // add the two values at the top of the stack.
                 // The sum is left on the stack.
                 generator.Emit(OpCodes.Ldc_I4, 0);
                 for (int i = 1; i <= theValue;i++)
                 {
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Add);
        
                 }
        
                 // return the value
                 generator.Emit(OpCodes.Ret);
        
                 //Encapsulate information about the method and
                 //provide access to the method's metadata
                 MethodInfo computeSumInfo =
                    typeof(IComputer).GetMethod("ComputeSum");
        
                 // specify the method implementation.
                 // Pass in the MethodBuilder that was returned 
                 // by calling DefineMethod and the methodInfo 
                 // just created
                 myType.DefineMethodOverride(simpleMethod, computeSumInfo);
        
                 // Create the type.
                 myType.CreateType(  );
                 return newAssembly;
              }
        
              // check if the interface is null
              // if so, call Setup.
              public double DoSum(int theValue)
              {
                 if (theComputer == null)
                 {
                    GenerateCode(theValue);
                 }
        
                 // call the method through the interface
                 return (theComputer.ComputeSum(  ));
              }
        
              // emit the assembly, create an instance 
              // and get the interface
              public void GenerateCode(int theValue)
              {
                 Assembly theAssembly = EmitAssembly(theValue);
                 theComputer = (IComputer) 
                    theAssembly.CreateInstance("BruteForceSums");
              }
        
              // private member data
              IComputer theComputer = null;
        
           }
        
           public class TestDriver
           {
              public static void Main(  )
              {
                 const int val = 2000;  // Note 2,000
        
                 // 1 million iterations!
                 const int iterations = 1000000;
                 double result = 0;
        
                 // run the benchmark
                 MyMath m = new MyMath(  ); 
                 DateTime startTime = DateTime.Now;            
                 for (int i = 0;i < iterations;i++)
                    result = m.DoSumLooping(val);
                 }
                 TimeSpan elapsed = 
                    DateTime.Now - startTime;
                 Console.WriteLine(
                    "Sum of ({0}) = {1}",val, result);
                 Console.WriteLine(
                    "Looping. Elapsed milliseconds: " + 
                    elapsed.TotalMilliseconds + 
                    " for {0} iterations", iterations);
        
                 // run our reflection alternative
                 ReflectionTest t = new ReflectionTest(  );
        
                 startTime = DateTime.Now; 
                 for (int i = 0;i < iterations;i++)
                 {
                    result = t.DoSum(val);
                 }
        
                 elapsed = DateTime.Now - startTime;
                 Console.WriteLine(
                    "Sum of ({0}) = {1}",val, result);
                 Console.WriteLine(
                    "Brute Force. Elapsed milliseconds: " + 
                    elapsed.TotalMilliseconds  + 
                    " for {0} iterations", iterations);
              }
           }
        }
        

        输出: (2000) 之和 = 2001000
        循环播放。经过的毫秒数:
        11468.75 1000000 次迭代
        (2000) 之和 = 2001000
        蛮力。经过的毫秒数:
        1000000 次迭代的 406.25

        Here 是整章的链接,如果您想了解更多信息。

        【讨论】:

        • 我真的很讨厌人们在不解释原因的情况下对答案投反对票。这不起作用吗?它不回答问题吗?
        【解决方案4】:

        您可以使用 Enum.GetNames() 和 Enum.GetValues() 来检索值并动态地向它们添加新值。尽管我建议您使用列表而不是枚举或重新考虑您的设计。有些东西闻起来不对劲。

        【讨论】:

        • 我们可以通过反射添加它们吗?
        • GETNames 和 GETValues 应该提示您不能使用该方法更改枚举...
        猜你喜欢
        • 2012-05-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多