【问题标题】:Custom C# Enum ToString() when generated dynamically动态生成时自定义 C# Enum ToString()
【发布时间】:2011-10-14 07:23:31
【问题描述】:

我在 C# ASP.NET 解决方案中从我的数据库中生成表示整数 ID 的动态枚举。我想要两件事,尽管两者都不可能。

1) 例如,我希望 .ToString() 方法给我“345”,而不是枚举的字符串名称(它表示为字符串的 int)。这个问题的每一个答案似乎都在增加

[Description="Blah"]
EnumName = 1

在声明之上并使用 GetDescription() 方法。我不知道如何使用我正在使用的动态代码来做到这一点。

2) 例如,我宁愿不强制转换为 int 来使用它,我宁愿 (Enum.Name == 5)。如果这不可能,我会投,但我真的不想使用 ((int)Enum.Name)).ToString();

这里是动态代码生成:

public static void Main()
{
    AppDomain domain = AppDomain.CurrentDomain;

    AssemblyName aName = new AssemblyName("DynamicEnums");
    AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save);

    ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

    List<Type> types = new List<Type>();

    foreach(ReferenceType rt in GetTypes())
    {
        EnumBuilder eb = mb.DefineEnum(rt.Name, TypeAttributes.Public, typeof(int));

        foreach (Reference r in GetReferences(rt.ID))
        {
            eb.DefineLiteral(NameFix(r.Name), r.ID);
        }

        types.Add(eb.CreateType());
    }

    ab.Save(aName.Name + ".dll");

    foreach (Type t in types)
    {
        foreach (object o in Enum.GetValues(t))
        {
            Console.WriteLine("{0}.{1} = {2}", t, o, ((int) o));
        }

        Console.WriteLine();
        //Console.ReadKey();
    }

    Console.WriteLine();
    Console.WriteLine("Dynamic Enums Built Successfully.");
}

public static string NameFix(string name)
{
    //Strip all non alphanumeric characters
    string r = Regex.Replace(name, @"[^\w]", "");

    //Enums cannot begin with a number
    if (Regex.IsMatch(r, @"^\d"))
        r = "N" + r;

    return r;
}

可能没有办法做我想做的事,而且我会被卡住:

(int)Countries.USA //For int value
((int)Countries.CAN).ToString() //For string representation of int value, ex. "354"

有什么想法吗?

【问题讨论】:

  • 我绝对确定这一定是一种通过反射生成描述属性的方法。
  • 我真的不明白你为什么使用ENUM。当您编写一些代码时,它非常有用,您可以使用一些具有好名字的值来告诉您它的真正用途。也许使用一些dictionaryenum 更好,因为您是动态构建它?
  • 我制作了 dll,然后在我的 webapps 中引用它,然后我绑定下拉列表/复选框/单选按钮列表以在我的数据库中具有 ID 值。然后我想做一些事情,比如 if (Report == Reports.SevenDay)
  • @Ehryk,如果数据库模式是动态从数据库中的元数据中读取它们,为什么不在代码背后声明枚举?
  • 部分疯狂的设计要求是某些管理员可以添加它们,我不希望与数据库的 ID 有硬性联系,因为有多个(开发、测试、产品)可能ID 不同。我用它来动态绑定它。有没有更好的办法?

标签: c# .net dynamic enums tostring


【解决方案1】:

您能否调整类型安全的枚举模式来满足您的需求?

public class MyEnum
{
    #region Enum Values

    // Pre defined values.    
    public static readonly MyEnum ValueOne = new MyEnum(0);
    public static readonly MyEnum ValueTwo = new MyEnum(1);

    // All values in existence.
    private static readonly Dictionary<int, MyEnum> existingEnums = new Dictionary<int, MyEnum>{{ValueOne.Value, ValueOne}, {ValueTwo.Value, ValueTwo}};

    #endregion

    #region Enum Functionality

    private readonly int Value;

    private MyEnum(int value)
    {
        Value = value;
    }

    public static MyEnum GetEnum(int value)
    {
        // You will probably want to make this thread-safe.
        if (!existingEnums.ContainsKey(value)) existingEnums[value] = new MyEnum(value);

        return existingEnums[value];
    }

    public override string ToString()
    {
        return Value.ToString();
    }

    #endregion
}

用法:

private void Foo(MyEnum enumVal)
{
  return "Enum Value: " + enumVal;  // returns "Enum Value: (integer here) 
}

或者:

MyEnum.GetValue(2) == MyEnum.GetValue(4); // false
MyEnum.GetValue(3) == MyEnum.GetValue(3); // true

【讨论】:

  • 有没有办法在动态生成中使用它? EnumBuilder eb = mb.DefineEnum(rt.Name, TypeAttributes.Public, typeof(MyEnum));
  • 创建完枚举后你想对它做什么?比较不同对象的值?您可以向上面的类添加一个 GetEnumInstance(int value) 方法,该方法返回该值的当前存在的对象(如果没有,则创建它)。我将添加更多代码。
  • 我想我可以做这样的事情。我可以有一个 Value() 和 String 属性来获取每个我想要的。但是有没有办法覆盖 == 以返回一个 int 或一个字符串? (或者甚至两者都取决于它被比较的对象?)我会调查一下。
  • 看看msdn.microsoft.com/en-us/library/ms173147(v=vs.90).aspx。我的简单建议是:enumValue == MyEnum.GetValue(int or string here);
【解决方案2】:

所以您真的想将Enum 值转换为string 而不是名称?转换为Enum 的底层类型是获取值的最简单方法。我认为你很难得到比((int)...) 更短或更简单的东西。

如果您不想“枚举”某些值,或者想做一些不同的事情,您可以创建自己的 YourEnum 类,它基本上内置了演员表,在需要时进行转换似乎更容易且更具可读性。

也许你真的想要一些常量。

const string Blah = "345";

编辑

我有另一个想法,你可以像这样为Enum写一个扩展方法,

public static class MyExtentions
{
    public static string ValueString(this Enum e)
    {
       var ut = Enum.GetUnderlyingType(e.GetType());
       var castToString = typeOf(MyExtentions).GetMethod("CastToString");
       var gcast = cast.MakeGenericMethod(ut);
       var gparams = new object[] {e};
       return gcast.Invoke(null, gparams).ToString();
    }

    public static string CastToString<T>(object o)
    {
        return ((T)o).ToString();
    }

}

有了这个,您可以在任何Enum 实例上调用ValueString() 并获取值的字符串。它显然使用了反射,因此性能不会令人惊叹,但我认为这对你来说并不重要。

【讨论】:

  • 是的,我想要整数值作为字符串。
  • 嗯,也许 const 会更好,但我有时使用 int,有时使用字符串,所以我想要 int 常量。我希望 Country.USA 返回 120,Country.USA.ToString() 为“120”。枚举和带有 const int 的命名类之间有什么区别?
  • @Ehryk,我认为我的编辑可能正是您想要的。
【解决方案3】:

既然您使用的是 Enum 类类型,那么使用 Enum.Format 方法怎么样。

例如:

enum EnumClassA {One, Two, Three, Four};

...

EnumClassA chosenValue = EnumClassA.Three;

Console.WriteLine("Chosen value is {0}", Enum.Format(typeof(EnumClassA), chosenValue, "d"));

这应该给出以下输出:

Chosen value is 2

编辑

另一种选择是:

EnumClassA.Three.ToString("d"); //Enum.Name.ToString("d")

这也给出了一个字符串值“2”。

** 编辑 **

当您进行比较以查看该值是否存在于您的枚举中时,如何使用返回布尔值的 Enum.IsDefined(enumType, value)?

Console.WriteLine("Does the enumClassA contain {0} result is {1}", 5, Enum.IsDefined(typeof(enumClassA), 5));

这给出了以下输出:

Does the enumClassA contain 5 result is False

【讨论】:

  • 我真的在寻找比 int casts 和 ToStrings 更简写的版本,这似乎有点长。我正在做的是匹配下拉列表选择的值,绑定到整数,返回为“354”。
  • @Ehryk - 为了使其更短,您可以为 Enum 创建一个扩展方法。公共静态字符串 MakeString(this Enum bla){ return Enum.Format(bla.GetType(), bla, "d"); }
  • 如果您只想查看下拉列表值是否存在于枚举类型中,请使用 Enum.IsDefined - 请参阅编辑后的答案。
【解决方案4】:

我知道该问题已被标记为已回答,但也许使用扩展程序可能有用?

它仍然允许您以这种方式使用生成的枚举。

ps。调用 GetString 时要小心——不要意外调用 ToString!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication1
{

    public enum YourEnum
    {
        Apples = 1,
        Pears = 2,
    }

    class Program
    {
        static void Main(string[] args)
        {
            foreach (var value in (YourEnum[])Enum.GetValues(typeof(YourEnum)))
            {
                int v = value.GetValue();
                Console.WriteLine("{0} = {1} (int)", value.ToString(), v);

                string s = value.GetString();
                Console.WriteLine("{0} = {1} (string)", value.ToString(), s);
            }
            Console.ReadLine();

            //Results:

            //Apples = 1 (int)
            //Apples = 1 (string)
            //Pears = 2 (int)
            //Pears = 2 (string)
        }
    }

    public static class EnumExtensions
    {
        public static int GetValue(this Enum value) 
        {
            return Convert.ToInt32(value);
        }

        public static string GetString(this Enum value)
        {
            return Convert.ToInt32(value).ToString();
        }
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-22
    • 2015-05-17
    • 2010-11-28
    • 2019-04-30
    • 2016-12-25
    • 1970-01-01
    • 2017-04-15
    相关资源
    最近更新 更多