web-dev-qa-db-ja.com

ユーザーフレンドリな文字列を含む列挙型ToString

私の列挙型は次の値で構成されています。

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

私はユーザーフレンドリーな方法でこれらの値を出力できるようにしたいです。
私は文字列から価値へ再び行くことができる必要はありません。

256
Boris Callens

私はSystem.ComponentModel名前空間から Description 属性を使います。単に列挙を飾る:

private enum PublishStatusValue
{
    [Description("Not Completed")]
    NotCompleted,
    Completed,
    Error
};

それからそれを取得するためにこのコードを使用します:

public static string GetDescription<T>(this T enumerationValue)
    where T : struct
{
    Type type = enumerationValue.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
    }

    //Tries to find a DescriptionAttribute for a potential friendly name
    //for the enum
    MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
    if (memberInfo != null && memberInfo.Length > 0)
    {
        object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attrs != null && attrs.Length > 0)
        {
            //Pull out the description value
            return ((DescriptionAttribute)attrs[0]).Description;
        }
    }
    //If we have no description attribute, just return the ToString of the enum
    return enumerationValue.ToString();
}
319
Ray Booysen

私はこれを拡張メソッドで行います。

public enum ErrorLevel
{
  None,
  Low,
  High,
  SoylentGreen
}

public static class ErrorLevelExtensions
{
  public static string ToFriendlyString(this ErrorLevel me)
  {
    switch(me)
    {
      case ErrorLevel.None:
        return "Everything is OK";
      case ErrorLevel.Low:
        return "SNAFU, if you know what I mean.";
      case ErrorLevel.High:
        return "Reaching TARFU levels";
      case ErrorLevel.SoylentGreen:
        return "ITS PEOPLE!!!!";
      default:
        return "Get your damn dirty hands off me you FILTHY APE!";
    }
  }
}
320
Will

何かが足りないかもしれませんが、Enum.GetNameの何が問題になっていますか?

public string GetName(PublishStatusses value)
{
    return Enum.GetName(typeof(PublishStatusses), value)
}

edit:ユーザフレンドリな文字列の場合は、国際化/ローカライズを行うために.resourceを通過する必要があります。enumキーに基づいた固定キーを使用するほうが、デコレータ属性よりも適切です。

64
annakata

説明を列挙値に変換する逆拡張メソッドを作成しました。

public static T ToEnumValue<T>(this string enumerationDescription) where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
        throw new ArgumentException("ToEnumValue<T>(): Must be of enum type", "T");

    foreach (object val in System.Enum.GetValues(type))
        if (val.GetDescription<T>() == enumerationDescription)
            return (T)val;

    throw new ArgumentException("ToEnumValue<T>(): Invalid description for enum " + type.Name, "enumerationDescription");
}
18
bjrichardson

ここで最も簡単な解決策はカスタム拡張メソッドを使うことです(少なくとも.NET 3.5では - あなたはそれを以前のフレームワークバージョンのための静的ヘルパーメソッドに変換することができます)。

public static string ToCustomString(this PublishStatusses value)
{
    switch(value)
    {
        // Return string depending on value.
    }
    return null;
}

ここでは、実際のenum値の名前(単にToStringを呼び出すことで取得できる)以外のものを返したいと思います。

17
Noldorin

他の記事はJavaです。 C#のEnumsにメソッドを入れることはできません。

このようなことをしてください。

PublishStatusses status = ...
String s = status.ToString();

列挙値に異なる表示値を使用したい場合は、AttributesとReflectionを使用できます。

12
Lemmy

クラス/参照型を避ける他のもっと原始的なオプション:

  • 配列メソッド
  • 入れ子になった構造体メソッド

配列メソッド

private struct PublishStatusses
{
    public static string[] Desc = {
        "Not Completed",
        "Completed",
        "Error"
    };

    public enum Id
    {
        NotCompleted = 0,
        Completed,
        Error
    };
}

使用法

string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed];

ネストされた構造体メソッド

private struct PublishStatusses
{
    public struct NotCompleted
    {
        public const int Id = 0;
        public const string Desc = "Not Completed";
    }

    public struct Completed
    {
        public const int Id = 1;
        public const string Desc = "Completed";
    }

    public struct Error
    {
        public const int Id = 2;
        public const string Desc = "Error";
    }            
}

使用法

int id = PublishStatusses.NotCompleted.Id;
string desc = PublishStatusses.NotCompleted.Desc;

更新(03/09/2018)

拡張方法と上記の最初のテクニックのハイブリッド。

列挙型は、それらが「所属する」場所(Originのソースに最も近い場所で、一般的なグローバル名前空間ではない場所)で定義することを好みます。

namespace ViewModels
{
    public class RecordVM
    {
        //public enum Enum { Minutes, Hours }
        public struct Enum
        {
            public enum Id { Minutes, Hours }
            public static string[] Name = { "Minute(s)", "Hour(s)" };
        }
    }
}

拡張メソッドは共通の領域に適しているように思われます。そして、enumの "ローカライズされた"定義は拡張メソッドをより冗長にします。

namespace Common
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum.Id id)
        {
            return RecordVM.Enum.Name[(int)id];
        }
    }   
}

列挙型の使用例とその拡張方法.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit;

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum.Id eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

注:Enumラッパー(およびName配列)を削除することを実際に決定しました。名前文字列は、ハードコーディングされるのではなくリソース(つまり、構成ファイルまたはDB)から来るのが最善だからです。拡張メソッドをViewModels名前空間に入れました(別の "CommonVM.cs"ファイルに入れました)。さらに.Idのことは全部気が散って面倒になります。

namespace ViewModels
{
    public class RecordVM
    {
        public enum Enum { Minutes, Hours }
        //public struct Enum
        //{
        //    public enum Id { Minutes, Hours }
        //    public static string[] Name = { "Minute(s)", "Hour(s)" };
        //}
    }
}

CommonVM.cs

//namespace Common
namespace ViewModels
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum id)
        {
            //return RecordVM.Enum.Name[(int)id];
            switch (id)
            {
                case RecordVM.Enum.Minutes: return "Minute(s)";                    
                case RecordVM.Enum.Hours: return "Hour(s)";
                default: return null;
            }
        }
    }   
}

列挙型の使用例とその拡張方法.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}
8
samis

最も簡単な方法は、この拡張クラスをプロジェクトに含めることです。プロジェクト内のすべての列挙型で機能します。

public static class EnumExtensions
{
    public static string ToFriendlyString(this Enum code)
    {
        return Enum.GetName(code.GetType(), code);
    }
}

使用法:

enum ExampleEnum
{
    Demo = 0,
    Test = 1, 
    Live = 2
}

...

ExampleEnum ee = ExampleEnum.Live;
Console.WriteLine(ee.ToFriendlyString());
8
Milan Švec

あなたは Humanizer パッケージと一緒に Humanize Enums possiblityを使うことができます。たとえば、

enum PublishStatusses
{
    [Description("Custom description")]
    NotCompleted,
    AlmostCompleted,
    Error
};

それからあなたは直接enumでHumanize拡張メソッドを使うことができます:

var st1 = PublishStatusses.NotCompleted;
var str1 = st1.Humanize(); // will result in Custom description

var st2 = PublishStatusses.AlmostCompleted;
var str2 = st2.Humanize(); // will result in Almost completed (calculated automaticaly)
6
Konrad Kokosa

Ray Booysenに関しては、このコードにはバグがあります。 ユーザーフレンドリな文字列を含むEnum ToString

列挙値には複数の属性を考慮する必要があります。

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
    {
        Type type = enumerationValue.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
        }

        //Tries to find a DescriptionAttribute for a potential friendly name
        //for the enum
        MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
        if (memberInfo != null && memberInfo.Length > 0)
        {
            object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
            {
                //Pull out the description value
                return ((DescriptionAttribute)attrs.Where(t=>t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description;
            }
        }
        //If we have no description attribute, just return the ToString of the enum
        return enumerationValue.ToString();
5
Joel MC
public enum MyEnum
{
    [Description("Option One")]
    Option_One
}

public static string ToDescriptionString(this Enum This)
{
    Type type = This.GetType();

    string name = Enum.GetName(type, This);

    MemberInfo member = type.GetMembers()
        .Where(w => w.Name == name)
        .FirstOrDefault();

    DescriptionAttribute attribute = member != null
        ? member.GetCustomAttributes(true)
            .Where(w => w.GetType() == typeof(DescriptionAttribute))
            .FirstOrDefault() as DescriptionAttribute
        : null;

    return attribute != null ? attribute.Description : name;
}
4
Diogo Freitas

列挙型を使用する代わりに、静的クラスを使用してください。

取り替える

private enum PublishStatuses{
    NotCompleted,
    Completed,
    Error
};

private static class PublishStatuses{
    public static readonly string NotCompleted = "Not Completed";
    public static readonly string Completed = "Completed";
    public static readonly string Error = "Error";
};

それはこのように使われるでしょう

PublishStatuses.NotCompleted; // "Not Completed"

一番上の「拡張方法」ソリューションを使用した問題:

プライベートenumは他のクラスの中でよく使われます。それがそれ自身のクラスになければならないので、拡張メソッドの解決策はそこでは有効ではありません。このソリューションはプライベートにすることも、別のクラスに埋め込むこともできます。

4
Asher Garland

私はたまたまVB.NETファンなので、これが私のバージョンで、DescriptionAttributeメソッドとextensionメソッドを組み合わせたものです。まず、結果:

Imports System.ComponentModel ' For <Description>

Module Module1
  ''' <summary>
  ''' An Enum type with three values and descriptions
  ''' </summary>
  Public Enum EnumType
    <Description("One")>
    V1 = 1

    ' This one has no description
    V2 = 2

    <Description("Three")>
    V3 = 3
  End Enum

  Sub Main()
    ' Description method is an extension in EnumExtensions
    For Each v As EnumType In [Enum].GetValues(GetType(EnumType))
      Console.WriteLine("Enum {0} has value {1} and description {2}",
        v,
        CInt(v),
        v.Description
      )
    Next
    ' Output:
    ' Enum V1 has value 1 and description One
    ' Enum V2 has value 2 and description V2
    ' Enum V3 has value 3 and description Three
  End Sub
End Module

基本的なもの:3つの値V1、V2およびV3を持つEnumTypeと呼ばれる列挙型。 「魔法」はSub Main()のConsole.WriteLine呼び出しで発生します。最後の引数は単にv.Descriptionです。これは、V1の場合は「One」、V2の場合は「V2」、V3の場合は「Three」を返します。このDescriptionメソッドは、実際にはEnumExtensionsという別のモジュールで定義されている拡張メソッドです。

Option Strict On
Option Explicit On
Option Infer Off

Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.ComponentModel

Module EnumExtensions
  Private _Descriptions As New Dictionary(Of String, String)

  ''' <summary>
  ''' This extension method adds a Description method
  ''' to all enum members. The result of the method is the
  ''' value of the Description attribute if present, else
  ''' the normal ToString() representation of the enum value.
  ''' </summary>
  <Extension>
  Public Function Description(e As [Enum]) As String
    ' Get the type of the enum
    Dim enumType As Type = e.GetType()
    ' Get the name of the enum value
    Dim name As String = e.ToString()

    ' Construct a full name for this enum value
    Dim fullName As String = enumType.FullName + "." + name

    ' See if we have looked it up earlier
    Dim enumDescription As String = Nothing
    If _Descriptions.TryGetValue(fullName, enumDescription) Then
      ' Yes we have - return previous value
      Return enumDescription
    End If

    ' Find the value of the Description attribute on this enum value
    Dim members As MemberInfo() = enumType.GetMember(name)
    If members IsNot Nothing AndAlso members.Length > 0 Then
      Dim descriptions() As Object = members(0).GetCustomAttributes(GetType(DescriptionAttribute), False)
      If descriptions IsNot Nothing AndAlso descriptions.Length > 0 Then
        ' Set name to description found
        name = DirectCast(descriptions(0), DescriptionAttribute).Description
      End If
    End If

    ' Save the name in the dictionary:
    _Descriptions.Add(fullName, name)

    ' Return the name
    Return name
  End Function
End Module

Reflectionを使用して記述属性を検索するのは遅いため、検索もプライベートのDictionaryにキャッシュされます。これは要求に応じて移入されます。

(VB.NETソリューションには申し訳ありませんが、C#に変換するのは比較的簡単なはずです。私のC#は、拡張機能のような新しい話題ではさびています)

2
MarkusT

上記の提案の要約をサンプルとともに整理します。

namespace EnumExtensions {

using System;
using System.Reflection;

public class TextAttribute : Attribute {
   public string Text;
   public TextAttribute( string text ) {
      Text = text;
   }//ctor
}// class TextAttribute

public static class EnumExtender {

public static string ToText( this Enum enumeration ) {

   MemberInfo[] memberInfo = enumeration.GetType().GetMember( enumeration.ToString() );

   if ( memberInfo != null && memberInfo.Length > 0 ) {

      object[] attributes = memberInfo[ 0 ].GetCustomAttributes( typeof(TextAttribute),  false );

      if ( attributes != null && attributes.Length > 0 ) {
         return ( (TextAttribute)attributes[ 0 ] ).Text;
      }

   }//if

   return enumeration.ToString();

}//ToText

}//class EnumExtender

}//namespace

使用法:

using System;
using EnumExtensions;

class Program {

public enum Appearance {

  [Text( "left-handed" ) ]
  Left,

  [Text( "right-handed" ) ]
  Right,

}//enum

static void Main( string[] args ) {

   var appearance = Appearance.Left;
   Console.WriteLine( appearance.ToText() );

}//Main

}//class
2
underscore

Enum.GetName を使用します

上記のリンクから...

using System;

public class GetNameTest {
    enum Colors { Red, Green, Blue, Yellow };
    enum Styles { Plaid, Striped, Tartan, Corduroy };

    public static void Main() {

        Console.WriteLine("The 4th value of the Colors Enum is {0}", Enum.GetName(typeof(Colors), 3));
        Console.WriteLine("The 4th value of the Styles Enum is {0}", Enum.GetName(typeof(Styles), 3));
    }
}
// The example displays the following output:
//       The 4th value of the Colors Enum is Yellow
//       The 4th value of the Styles Enum is Corduroy
1
J.M.H

より簡潔なまとめ:

using System;
using System.Reflection;

public class TextAttribute : Attribute
{
    public string Text;
    public TextAttribute(string text)
    {
        Text = text;
    }
}  

public static class EnumExtender
{
    public static string ToText(this Enum enumeration)
    {
        var memberInfo = enumeration.GetType().GetMember(enumeration.ToString());
        if (memberInfo.Length <= 0) return enumeration.ToString();

        var attributes = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);
        return attributes.Length > 0 ? ((TextAttribute)attributes[0]).Text : enumeration.ToString();
    }
}

アンダースコアの説明と同じ使い方。

1
StefanDK

これは、一般的なGetCustomAttributesメソッドとLINQを使用して物事を少しきれいにする、Ray Booysenのコードの更新です。

    /// <summary>
    /// Gets the value of the <see cref="T:System.ComponentModel.DescriptionAttribute"/> on an struct, including enums.  
    /// </summary>
    /// <typeparam name="T">The type of the struct.</typeparam>
    /// <param name="enumerationValue">A value of type <see cref="T:System.Enum"/></param>
    /// <returns>If the struct has a Description attribute, this method returns the description.  Otherwise it just calls ToString() on the struct.</returns>
    /// <remarks>Based on http://stackoverflow.com/questions/479410/enum-tostring/479417#479417, but useful for any struct.</remarks>
    public static string GetDescription<T>(this T enumerationValue) where T : struct
    {
        return enumerationValue.GetType().GetMember(enumerationValue.ToString())
                .SelectMany(mi => mi.GetCustomAttributes<DescriptionAttribute>(false),
                    (mi, ca) => ca.Description)
                .FirstOrDefault() ?? enumerationValue.ToString();
    }   
1
Richard Hein

フラグenumを含む。

    public static string Description(this Enum value)
    {
        Type type = value.GetType();

        List<string> res = new List<string>();
        var arrValue = value.ToString().Split(',').Select(v=>v.Trim());
        foreach (string strValue in arrValue)
        {
            MemberInfo[] memberInfo = type.GetMember(strValue);
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
                {
                    res.Add(((DescriptionAttribute)attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description);
                }
                else
                    res.Add(strValue);
            }
            else
                res.Add(strValue);
        }

        return res.Aggregate((s,v)=>s+", "+v);
    }
0
Lukich