【问题标题】:java enums vs C# enums - missing featuresjava enums vs C# enums - 缺少功能
【发布时间】:2012-11-18 22:15:39
【问题描述】:

在 java 中,我可以很容易地用附加数据描述一个枚举。
我可以这样描述它

public enum OperatorType
{
    GreaterOrEqual  (">=", "GreaterOrEqual"),
    Greater         (">" ,"Greater"),
    Less            ("<", "Less"),
    LessOrEqual     ("<=", "LessOrEqual"),
    Equal           ("==", "Equal"),
    Between         ("Between", "Between"),
    Around          ("Around","Around");

    private final String symbol;
    private final String name;

    private OperatorType(final String symbol, final String name) {
        this.symbol = symbol;
        this.name = name;
    }
}

然后添加一个迭代 values() 的静态方法,将所有数据添加到 hashmap 并允许通过其属性之一作为键从映射中检索完整的枚举数据。

简而言之,枚举是java中一个非常发达的类型。

现在,
迁移到 C#,我有什么选择?
我想保存一个枚举及其属性,将其加载到地图中,并在需要时按键检索。我有什么可以帮助的吗(例如,每个枚举都有一个单音 - 这不是一个好主意)。
谢谢。

【问题讨论】:

  • C# 不支持这样的东西。对不起。
  • java 是一只害群之马。你可以做一些数据容器,只使用枚举作为键。
  • @Toni:害群之马?这个功能太棒了。
  • 但是对于您的特定用例,您不需要在枚举中包含那种东西。只需在(帮助程序)类中提供一个静态方法,例如PopulateOperatorMapping,在其中你用枚举到字符串元组的映射填充字典,你应该没问题。
  • Java 风格枚举的最大优点是它们是类型安全的:编译器强制该类型的任何有效实例始终是枚举值之一。另一方面,在 C# 中,枚举实际上只是简单的 int 之上的少量语法糖。

标签: c# java enums


【解决方案1】:

我会创建一个类,其中包含每种类型的 public static readonly 实例并完全放弃枚举。您可以将它们用作字典键或做任何您喜欢的事情。如果您仍打算将它们映射到基础数据类型 (int),那么您也可以为此创建隐式运算符。

public class OperatorType
{
    private static readonly Dictionary<int, OperatorType> OperatorMapping = new Dictionary<int, OperatorType>();

    public static readonly OperatorType GreaterOrEqual  = new OperatorType(0, ">=", "GreaterOrEqual");
    public static readonly OperatorType Greater         = new OperatorType(1, ">", "Greater");



    public readonly String symbol;
    public readonly String name;
    private readonly int underlyingValue;

    private OperatorType(int underlyingValue, string symbol, string name) {
        this.underlyingValue = underlyingValue;
        OperatorMapping[underlyingValue] = this;

        this.symbol = symbol;
        this.name = name;
    }

    public static implicit operator int(OperatorType operatorType)
    {
        return operatorType.underlyingValue;
    }

    public static implicit operator OperatorType(int value)
    {
        return OperatorMapping[value];
    }
}

示例用法:

Dictionary<OperatorType, string> operators = new Dictionary<OperatorType, string>();

operators.Add(OperatorType.GreaterOrEqual, "Greater or equal");

Console.WriteLine(operators[OperatorType.GreaterOrEqual]); //"Greater or equal"

OperatorType operatorType = 1;

Console.WriteLine(operatorType.name); //"Greater"

如果您不关心潜在价值,请不要包含它。还要考虑Dictionary 映射对于您的使用是否应该是线程安全的。如果需要,您还可以公开静态 IEnumerable&lt;OperatorType&gt;(或其他集合)来定义所有运算符。

编辑:再想一想,explicit 运算符可能比 implicit 更可取,既符合典型的 .NET 最佳实践,又能更好地匹配典型的 enum 转换。

【讨论】:

  • 非常好的解决方案,克里斯,谢谢。
  • 不错的解决方案,但是在比较这些值时它是如何工作的?通常你可以检查object.OperatorType == OperatorType.Equal,但我认为这在这种情况下不起作用,因为你使用的是对象而不是枚举。有什么想法吗?
  • @Sander:它仍然可以正常工作,因为它将使用对象引用相等性并且(理论上)每个运算符都作为单例存在。如果不是这种情况,则可以实现 IEquatable&lt;OperatorType&gt; 并覆盖 Object.Equals 和相等 == 运算符以检查底层整数值。
  • 嗯,有道理。每个运算符都充当单例是真的吗?我不知何故假设每次调用OperatorType.Type 都会创建一个OperatorType 的新实例。
  • @Sander:不,在这种情况下,它们被声明为static readonly fields。这意味着它们在第一次访问类型时被分配一次(作为我在答案中写的字段初始化程序,或者在静态构造函数中)并在应用程序的生命周期内持续存在。通过将实例构造函数设为私有,这意味着OperatorType 的用户可以使用/引用/访问公开的初始运算符集。
【解决方案2】:

最方便的解决方法可能是为您的枚举类型创建一个扩展方法,并返回相关的符号。

类似这样的:

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            tester t = tester.x;
            t.testenums();
            Console.ReadKey();
        }


    }

    public static class ext
    {
        public static void testenums(this tester x)
        {
            Console.WriteLine(x.ToString());
        }
    }

    public enum tester
    {
        x,
        y
    }
}

当然你可以写一个更复杂的扩展方法,有返回值等等,这只是一个例子。

【讨论】:

【解决方案3】:

你可以创建一个属性:

public class EnumKeyAttribute : Attribute
{
    public string Key { get; set; }

    public string Description { get; set; }

    public EnumKeyAttribute(string key, string description)
    {
        this.Key = key;
        this.Description = description;
    }
}

然后将其应用于您的枚举

public enum OperatorType
{
    [EnumKey(">=", "GreaterOrEqual")]
    GreaterOrEqual,

    [EnumKey(">", "Greater")]
    Greater,

    [EnumKey("<", "Less")]
    Less,

    [EnumKey("<=", "LessOrEqual")]
    LessOrEqual,

    [EnumKey("==", "Equal")]
    Equal,

    [EnumKey("Between", "Between")]
    Between,

    [EnumKey("Around", "Around")]
    Around
}

要获取属性数据,您可以使用反射。下面是获取“Less”属性的示例

        MemberInfo memberInfo = typeof(OperatorType).GetMember(OperatorType.Less.ToString()).FirstOrDefault();

        if(memberInfo != null)
        {
            EnumKeyAttribute attribute = (EnumKeyAttribute)memberInfo.GetCustomAttributes(typeof(EnumKeyAttribute), false).FirstOrDefault();

            Console.WriteLine(attribute.Key);
            Console.WriteLine(attribute.Description);
        }

但是因为这些枚举不是在运行时创建的,所以您可以通过创建一个在字典中查找值的静态方法来提高效率。将此作为易于使用的扩展方法

public static class KeyFinder
{
    private static Dictionary<OperatorType, EnumKeyAttribute> lookupTable =
        new Dictionary<OperatorType, EnumKeyAttribute>();

    public static EnumKeyAttribute GetKey(this OperatorType type)
    {

        if (lookupTable.ContainsKey(type))
        {
            return lookupTable[type];
        }

        MemberInfo memberInfo = typeof(OperatorType).GetMember(type.ToString()).FirstOrDefault();

        if (memberInfo != null)
        {
            EnumKeyAttribute attribute = (EnumKeyAttribute)memberInfo.GetCustomAttributes(typeof(EnumKeyAttribute), false).FirstOrDefault();

            if (attribute != null)
            {
                lookupTable.Add(type, attribute);
                return attribute;
            }
        }

        // add a null value so next time it doesn't use reflection only to find nothing
        lookupTable.Add(type, null);

        return null;
    }
}

所以现在要获取值,您只需执行以下操作:

OperatorType.Less.GetKey().Key
OperatorType.Less.GetKey().Description

请注意 null 引用异常(因为如果找不到属性,它将返回 null)。如果您想通过键查找,您可以简单地创建使用字符串值作为键的其他扩展方法。

【讨论】:

    【解决方案4】:

    C# 并没有真正的相同功能。然而,有几种可能性可以真正接近(并且可能更灵活)。

    坚持常规枚举,您可以使用属性来丰富额外的信息。当然,这需要反思才能使用

    public enum OperatorType
    {
        [DisplayName(">=")]
        GreaterOrEqual,
        // ...
    }
    

    有几种模式可以解决这个问题,例如http://www.codeproject.com/Articles/28087/DisplayNameAttribute-for-Enumerations,谷歌了解更多。

    另一种方法是使用常规类增强枚举类型:

    public class OperatorType
    {
       public static OperatorType GreaterOrEqual = new OperatorType(">=", "GreaterOrEqual");
       // ...
    
       string symbol;
       string name;
       private OperatorType(string symbol, string name)
       {
           this.symbol = symbol;
           this.name = name;
       }
    }
    

    This article 描述了在 C# 中使用类枚举类型的其他一些方法

    【讨论】:

      【解决方案5】:

      如果你真的需要 C# 中 Java 风格的枚举的功能,我看到了三种合理的方法来实现它:

      1. 使用 C# 枚举和辅助方法的静态类。你失去了类型安全,但这是一个非常可行的解决方案。

      2. 使用 C# 枚举和一组扩展方法。可能是最惯用的 C# 解决方案,但您仍然必须处理类型安全的损失(您的扩展方法应该能够处理超出范围的值,即使只是通过抛出异常)。

      3. 在 Java 5 中获得 enum 关键字之前,使用 Java 中常见的 type-safe enum pattern。如果您对每个枚举值都有重要的逻辑,这将是我的首选。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-04
        • 2011-06-07
        • 2010-10-18
        • 2022-12-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多