如何在C#枚举中使用特殊字符?

17
例如:
public enum Unit{
  KW,
  kV,
  V,
  Hz,
  %V
}

在这种情况下,百分号(%)是一个特殊字符。那么,我该如何将这个字符放入枚举中?


7
短回答:不能...长回答:你无法... - Sani Huttunen
我不确定为什么你需要在枚举中使用特殊字符,但如果你像我一样需要显示更好的名称,可以使用XmlEnumAttribute值来代替类型。请查看我的博客以获取更多详细信息:http://www.bryanavery.co.uk/post/2010/01/08/How-do-you-retrieving-the-XmlEnumAttribute-values-for-an-Enum.aspx - Coppermill
1
即使你可以这样做(基于下面可能的答案),这真的是非常非常糟糕的实践,因为每次尝试使用它时,都会使代码可读性变得混乱。 - Nick
7个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
35
即使你能做到这一点(看起来你做不到),这也可能不是一个好主意,因为你会将枚举应该如何显示与操作它的程序代码混合在一起。更好的选择是定义一个属性(或使用现有的DisplayNameAttribute),并将你的枚举注释为附加的元数据:
public enum Unit{ 
  [DisplayName("Hz")] Hertz, 
  [DisplayName("%V")] Volt 
} 

3
赞同这种方法。我们使用DisplayKeyAttribute将枚举值与可本地化的字符串关联起来。 - moribvndvs
17
因为使用DisplayName时会收到错误消息:“Attribute 'DisplayName' is not valid on this declaration type. It is only valid on 'class, method, property, indexer, event' declarations.”,所以我改用了Description属性。 - lcrepas

12

枚举成员不应用于用户界面显示目的。它们应该映射到一个字符串以便显示。你可以创建一个字符串数组(或字典)来将每个枚举成员映射到一个字符串用于用户交互。

话虽如此,直接回答你的问题,你可以使用\uxxxxV,其中xxxx是表示%的Unicode代码点的十六进制数。但这远非推荐做法。正如Henk指出的那样,这对于%是行不通的,因为它不属于Unicode类Lu、Ll、Lt、Lm、Lo、Nl、Mn、Mc、Nd、Pc、Cf(字母、数字、连接和格式化字符)。只有这些字符是可接受作为标识符的字符。


2
如果 \xxx 不被视为字母字符(% 也不是),这仍会导致编译错误。% = \0025 - H H
@AMissico:并不总是可行的。例如,当你有 [Flags] 枚举类型时,枚举类型的值与枚举成员之间就不存在一对一的关系。 - Mehrdad Afshari
@Mehrdad,没错,但通常标志枚举不会以字符串形式呈现给用户。 - AMissico
@AMissico: 我可以看到一些情况下它们是有用的。另外,我更喜欢命令式映射(而不是声明式映射,在很多情况下都很有用)的另一个原因是从字符串表示返回枚举值更容易。 - Mehrdad Afshari
我同意。你也可以用自定义属性装饰那些枚举,而不是使用字典:[MyCustomAttribute(“一些字符串”)] - tigrou
显示剩余3条评论

7

仅仅是为了注册另一种简单的方法,你可以使用常量定义“你自己”的枚举器。在你的例子中

public class UnitEnum
{
  public const string KW = "KW";
  public const string Volt = "%V";
}

要访问它,只需输入:UnitEnum.Volt


4

这个答案与@Coppermill的答案有关,我觉得在处理枚举时使用DescriptionAttribute更具语义性。

public enum ReportStatus
{
    [Description("Reports that are running")] Running,
    [Description("Reports that are pending to run")] Pending,
    [Description("Reports that have errored while running")] Error,
    [Description("Report completed successfully.")] Finished
}
然后我可以这样读取它:
    public static bool IsNullable(this Type type)
    {
        if (!type.IsGenericType)
            return false;
        var g = type.GetGenericTypeDefinition();
        return (g.Equals(typeof (Nullable<>)));
    }

    public static Type ConcreteType(this Type type)
    {
        if (IsNullable(type))
            type = UnderlyingTypeOf(type);
        return type;
    }

.

    public static string ReadDescription<T>(T enumMember)
    {
        if (typeof (T).IsNullable() && enumMember == null) return null;

        var type = (typeof (T).ConcreteType());

        var fi = type.GetField(enumMember.ToString());

        var attributes = fi.GetCustomAttributes(typeof (DescriptionAttribute), false);

        if(attributes.Length == 0) return enumMember.ToString();

        return attributes.Cast<DescriptionAttribute>().First().Description;
    }

接下来的使用方法为ReadDescription(ReportStatus.Running),我还有一个方法可以将枚举转换为KeyValuePair Enumerable以将枚举绑定到DropDown。


多年后的跟进。除非我真的打算使用 [Flags],否则我已经停止使用枚举。此时,我使用静态类和强类型代替。请参见 https://dev59.com/IXfZa4cB1Zd3GeqPXOJ7 - Chris Marisic

0
抱歉,我刚意识到自己没有回答问题。我不会删除我的回答,因为有人可能会发现这些代码片段有用。

我完全同意Tomas Petricek的观点,因此我不会重复他的答案。

这是我的解决方案。我已经使用这个代码约五年了。我决定创建一个自定义属性,以便在标题和其他方面使用DisplayName属性。


Public Module MainModule
    Public Sub Main()
        Console.WriteLine(EnumEx.GetNumberFormatString(Unit.Volt), 120.13)
    End Sub
End Module

Public Enum Unit
    <NumberFormatString("{0} Hz"), DisplayName("Hertz")> Hz
    <NumberFormatString("{0} %V"), DisplayName("%Volt")> pV
End Enum

<AttributeUsage(AttributeTargets.All)> _
Public NotInheritable Class NumberFormatStringAttribute
    Inherits Attribute

    Public Shared ReadOnly [Default] As NumberFormatStringAttribute = New NumberFormatStringAttribute

    Private _format As String

    Public Sub New()
        Me.New(Char.MinValue)
    End Sub

    Public Sub New(ByVal format As String)
        _format = format
    End Sub

    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        If (obj Is Me) Then
            Return True
        End If
        Dim oAttribute As NumberFormatStringAttribute = TryCast(obj, NumberFormatStringAttribute)
        If (Not oAttribute Is Nothing) Then
            Return (oAttribute.NumberFormatString = Me.NumberFormatString)
        End If
        Return False
    End Function

    Public Overrides Function GetHashCode() As Integer
        Return Me.NumberFormatString.GetHashCode
    End Function

    Public Overrides Function IsDefaultAttribute() As Boolean
        Return Me.Equals(NumberFormatStringAttribute.Default)
    End Function

    Public ReadOnly Property NumberFormatString() As String
        Get
            Return Me.NumberFormatStringValue
        End Get
    End Property

    Private Property NumberFormatStringValue() As String
        Get
            Return _format
        End Get
        Set(ByVal value As String)
            _format = value
        End Set
    End Property

End Class

Public NotInheritable Class EnumEx

    Private Sub New()
    End Sub

    Public Shared Function GetNumberFormatString(ByVal value As Object) As String
        Dim sResult As String = Nothing
        Dim oFieldInfo As System.Reflection.FieldInfo = value.GetType.GetField(value.ToString)
        If Not (oFieldInfo Is Nothing) Then
            Dim oCustomAttributes() As Object = oFieldInfo.GetCustomAttributes(GetType(NumberFormatStringAttribute), True)
            If (Not (oCustomAttributes Is Nothing)) AndAlso oCustomAttributes.Length > 0 Then
                sResult = DirectCast(oCustomAttributes(0), NumberFormatStringAttribute).NumberFormatString
            End If
        End If
        Return sResult
    End Function

End Class

0

枚举成员不应用于用户界面显示目的。但是您可以使用以下的简单技巧,通过DisplayName("Your Property Display Name")来实现此目的。

//Enum
public enum ErrorCodes
{
      [DisplayName("U-volt")] UVolt = 2,
      [DisplayName("L-trip")] Ltrip = 5
}


//Here you can retrive Display name as bellow
public class MyClass{
     public void readEnumDisplayName(){
         String ErroCode = Enum.GetName(typeof(ErrorCodes), 5);    // Find Error Name using Integer value
          MemberInfo property = typeof(ErrorCodes).GetRuntimeField(ErroCode);     //Find Field
          var dd = property.GetCustomAttribute(typeof(DisplayNameAttribute)) as DisplayNameAttribute;   //Find Display Attribute
          if (dd != null)  //Check if null
          {
             var name = dd.DisplayName;   //Retrive Display name
          }
      }
}

-2
有些人可能会说枚举只适用于代码,但我不同意,我使用它来编码和显示功能。 在您的特定情况下,我会使用完整的单词。
public enum UnitType {
  Kilowatt,
  Kilovolt,
  Volt,
  Hertz,
  Ohm,
  Faraday
}

所以我可以将它们用作下拉菜单中的选项(例如,当我需要创建一个新项目时,我只需要将该项目附加到枚举中即可...)

ddl.Items.Clear();
foreach (string type in Enum.GetNames(typeof(UnitType)))
    ddl.Items.Add(type);

我倾向于使用空格分隔符,但通常使用下划线来替代空格,例如:

public enum myType { Process_Time, Process_Order, Process_Invoices }

而 DropDownList 的项将会是

ddl.Items.Add(type.Replace("_", " "));
当我想从下拉列表中设置类型时,我使用解析。
UnitType unit = (UnitType)Enum.Parse(
                                 typeof(UnitType),
                                 ddl.SelectedValue.toString());

当然,如果您使用分隔符

 ddl.SelectedValue.toString().Replace(" ", "_"));

一些规则需要考虑,以编写更好的代码

  • 总是对枚举类型Type进行写作,在您的情况下Unit应该是UnitType
  • 使用首字母大写形式来表示枚举对象

提醒一下

  • 您可以在位操作中使用枚举,并添加[Flags]关键字
  • 如果您不想拥有0、1、2、3...,您可以指定枚举的整数值

我希望我能帮助到某个人。


2
由于你在演示目的上硬编码了实际代码,所以遭到了负评。 - AMissico
2
你应该抛弃任何推荐你这样做的重构书籍。 你的代码脆弱且难以重构,因为改变枚举会导致用户界面出现问题 - 而且我怀疑你在那个层面上没有集成测试,所以它很可能会上线。 - Alun Harford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,