列挙型の文字列表現


912

次の列挙があります。

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

ただし、問題は、ID 1ではなくAuthenticationMethod.FORMSを要求するときに「FORMS」という単語が必要になることです。

この問題の次の解決策を見つけました(リンク):

最初に、「StringValue」というカスタム属性を作成する必要があります。

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

次に、この属性を列挙子に追加できます。

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

そしてもちろん、そのStringValueを取得するために何かが必要です。

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

これで、列挙子の文字列値を取得するツールが手に入りました。その後、次のように使用できます。

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

さて、これらすべては魅力のように動作しますが、私はそれがたくさんの作業だと思います。これにはもっと良い解決策があるのか​​と思っていました。

私はまた、辞書と静的プロパティを使って何かを試しましたが、それも良くありませんでした。


8
いいね!これを使用して、列挙値をローカライズされた文字列に変換できます。
ØyvindSkaar

5
あなたはこれが長続きすると感じるかもしれませんが、実際には他のもののために行くにはかなり柔軟な方法です。私の同僚の一人が指摘したように、これは多くの場合、データベースコードを列挙値などにマップするEnumヘルパーを置き換えるために使用できます...
BenAlabaster

27
MSDNは、「Attribute」接尾辞を持つ接尾辞属性クラスを修正します。したがって、「class StringValueAttribute」;)
serhio

14
@BenAlabasterに同意します。これは実際には非常に柔軟です。また、静的メソッドののthis前に追加するだけで、これを拡張メソッドにすることができEnumます。その後、次のことができます AuthenticationMethod.Forms.GetStringValue();
Justin Pihony

5
このアプローチでは、リフレクションを使用して属性値を読み取りますが、私の経験ではGetStringValue()を何度も呼び出す必要がある場合は非常に遅くなります。タイプセーフ列挙型パターンの方が高速です。
Rn222 2014

回答:


868

タイプセーフ列挙型パターンを試してください。

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");        

    private AuthenticationMethod(int value, String name){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

更新 明示的(または暗黙的な)型変換することによって行うことができます

  • マッピング付きの静的フィールドの追加

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    • nb「列挙型メンバー」フィールドの初期化でインスタンスコンストラクターを呼び出すときにNullReferenceExceptionがスローされないようにするには、クラスの「列挙型メンバー」フィールドの前に必ず辞書フィールドを配置してください。これは、静的フィールド初期化子が宣言順に呼び出され、静的コンストラクターの前に、すべての静的フィールドが初期化される前、および静的コンストラクターが呼び出される前にインスタンスコンストラクターを呼び出すことができるという、奇妙で必要で混乱を招く状況が発生するためです。
  • インスタンスコンストラクタでこのマッピングを埋める

    instance[name] = this;
  • ユーザー定義の型変換演算子の追加と追加

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }

17
列挙型のように見えますが、列挙型ではありません。AuthenticationMethodsを比較しようとすると、興味深い問題が発生することは想像できます。おそらく、さまざまな等価演算子もオーバーロードする必要があります。
Ant

36
@ Ant:する必要はありません。各AuthenticationMethodのインスタンスは1つしかないため、Objectから継承された参照の等価性は問題なく機能します。
JakubŠturc2010

10
@tyriker:コンパイラはそうします。コンストラクタはプライベートなので、新しいインスタンスを作成できません。また、静的メンバーにはインスタンスからアクセスできません。
JakubŠturc10年

21
@Jakub非常に興味深い。私はそれを使って、その使い方を理解し、その利点を実感する必要がありました。これはパブリックな非静的クラスですが、インスタンス化することはできず、静的メンバーにのみアクセスできます。基本的には、列挙型のように動作します。しかし、最良の部分...静的メンバーはクラスの型であり、一般的な文字列や整数ではありません。それは... [それを待つ] ...タイプセーフな列挙型です!理解してくれてありがとう。
tyriker

6
@kiran以下のJakubŠturcの回答のわずかに変更されたバージョンを投稿しました。これにより、Switch-Caseステートメントで使用できるため、このアプローチにはマイナス面がありません:)
deadlydog '20

228

使用方法

Enum.GetName(Type MyEnumType,  object enumvariable)  

のように(仮定Shipperは定義された列挙型です)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

Enumクラスには調査する価値のある他の静的メソッドもたくさんあります...


5
丁度。文字列の説明にカスタム属性を作成しましたが、これは、コンボボックスなどに簡単にバインドできるユーザーフレンドリーバージョン(スペースやその他の特殊文字を含む)が必要だからです。
lc。

5
Enum.GetNameは、列挙型のフィールド名を反映します-.ToString()と同じです。パフォーマンスに問題がある場合は、問題になる可能性があります。列挙型のロードを変換しているのでない限り、心配する必要はありません。
キース

8
追加の機能を備えた列挙型が必要な場合に考慮すべき別のオプションは、構造体を使用して「独自のロール」を行うことです...構造体の個々のインスタンスを生成するコンストラクターに初期化される列挙値を表す静的な読み取り専用の名前付きプロパティを追加します...
Charles Bretana、2009年

1
次に、必要な他の構造体メンバーを追加して、この「列挙型」に必要な機能を実装することができます...
Charles Bretana

2
ここでの問題は、GetNameがローカライズできないことです。これは常に問題になるわけではありませんが、注意が必要です。
Joel Coehoorn、2009年

79

ToString()を使用して、値ではなく名前を参照できます

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

ドキュメントはここにあります:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

...列挙型にPascalケースで名前を付ける場合(私が行うように-ThisIsMyEnumValue = 1など)、非常に単純な正規表現を使用して、わかりやすいフォームを印刷できます。

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

これは、任意の文字列から簡単に呼び出すことができます。

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

出力:

クレイジーパスカルケースセンテンスをフレンドリーケースに変換

これにより、家中を走り回ってカスタム属性を作成し、それらをenumにアタッチしたり、ルックアップテーブルを使用してenum値とフレンドリーな文字列を結び付けたり、何よりも自己管理したりして、無限のPascal Case文字列で使用できます。より再利用可能。もちろん、それはあなたが別のものを持つことはできませんソリューションが提供する列挙型とフレンドリ名。

ただし、より複雑なシナリオでは、元のソリューションが好きです。ソリューションをさらに一歩進めて、GetStringValueを列挙型の拡張メソッドにすると、StringEnum.GetStringValueのようにそれを参照する必要がなくなります...

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  FieldInfo fi = type.GetField(value.ToString());
  StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
  if (attrs.Length > 0)
    output = attrs[0].Value;
  return output;
}

次に、enumインスタンスから簡単に直接アクセスできます。

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

2
「フレンドリ名」にスペースが必要な場合、これは役に立ちません。「フォーム認証」など
Ray Booysen

4
したがって、列挙の名前にFormsAuthenticationのようなキャップを付けて、先頭にないキャップの前にスペースを挿入してください。文字列にスペースを挿入するのはロケット科学ではありません...
BenAlabaster 2009年

4
Pascalケース名の自動スペースは、大文字で表記する必要がある省略形(XMLやGPSなど)が含まれている場合に問題になります。
Richard Ev

2
@RichardEv、これには完璧な正規表現はありませんが、ここでは略語を使用すると少しうまくいくはずです。"(?!^)([^A-Z])([A-Z])", "$1 $2"。そうHereIsATESTなりましたHere Is ATEST
スペアバイト2013

彼らがそうであるこれらの小さな「ハック」を行うことは有能ではありません。OPの言っていることがわかり、Enumsの優雅さを使用しながら、関連するメッセージに簡単にアクセスできるようにして、同様の解決策を見つけようとしています。私が考えることができる唯一の解決策は、列挙名と文字列値の間にある種のマッピングを適用することですが、それは文字列データを維持する問題を回避しません(ただし、マルチリージョンが必要なシナリオでは実用的です) )
Tahir Khalid 2017

72

残念ながら、列挙型の属性を取得するためのリフレクションは非常に低速です。

この質問を参照してください:列挙値のカスタム属性にすばやくアクセスする方法を知っている人はいますか?

.ToString()あまりにも列挙型ではかなり遅いです。

列挙型の拡張メソッドを書くこともできます:

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

これは素晴らしいことではありませんが、迅速であり、属性やフィールド名の反映を必要としません。


C#6アップデート

C#6を使用できる場合、新しいnameof演算子は列挙型で機能するため、コンパイル時ににnameof(MyEnum.WINDOWSAUTHENTICATION)変換され"WINDOWSAUTHENTICATION"、列挙型名を取得する最も速い方法になります。

これは、明示的な列挙型をインライン化された定数に変換するため、変数にある列挙型では機能しないことに注意してください。そう:

nameof(AuthenticationMethod.FORMS) == "FORMS"

だが...

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"

24
属性値を一度フェッチして、Dictionary <MyEnum、string>に配置すると、宣言的な側面を維持できます。
Jon Skeet、

1
ええと、反射がボトルネックであることがわかったときに、多くの列挙型を使用するアプリでそれを行っていました。
キース

ジョンとキースのおかげで、私はあなたの辞書の提案を使用することになりました。うまく動作します(そして高速です!)。
Helge Klein

@JonSkeet私はこれが古いことを知っています。しかし、どうすればこれを達成できますか?
user919426

2
@ user919426:欲しいですか?辞書に入れますか?理想的には、コレクション初期化子を使用して辞書を作成するだけです。何を求めているのか明確ではありません。
Jon Skeet

59

私は拡張メソッドを使用します:

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

で飾りますenum

public enum AuthenticationMethod
{
    [Description("FORMS")]
    FORMS = 1,
    [Description("WINDOWSAUTHENTICATION")]
    WINDOWSAUTHENTICATION = 2,
    [Description("SINGLESIGNON ")]
    SINGLESIGNON = 3
}

あなたが電話するとき

AuthenticationMethod.FORMS.ToDescription()あなたが得るでしょう"FORMS"


1
私は追加する必要がありましたusing System.ComponentModel;あなたは文字列の値が列挙型の名前と同じにしたい場合にも、この方法はのみ動作します。OPは別の値を求めていました。
elcool、

2
あなたが電話するときを意味しません AuthenticationMethod.FORMS.ToDescription()か?
nicodemus13 14

41

ToString()メソッドを使用するだけ

public enum any{Tomato=0,Melon,Watermelon}

文字列を参照するにはTomato、次を使用します

any.Tomato.ToString();

ワオ。それは簡単でした。OPがカスタム文字列の説明を追加したかったのはわかっていますが、これが私が必要としたものです。振り返ってみると、これを試すことは知っていたはずですが、Enum.GetNameルートを下っていきました。
Rafe、2012

7
他の誰もがこれを過度に複雑にしているのはなぜですか?
ブレント14

18
@Brentほとんどの場合、.ToString()必要なユーザーフレンドリーな値とは異なる値を持っているためです。
Novitchi S 2014

2
@ブレント-これは、尋ねられる質問とは異なるためです。質問されるのは、列挙値が割り当てられている変数からこの文字列を取得する方法です。これは実行時に動的です。これは、型の定義をチェックし、実行時に設定されます。
ホーガン

1
@Hogan-ToString()は変数でも機能します: any fruit = any.Tomato; string tomato = fruit.ToString();
LiborV

29

.Net 4.0以降では、これに対する非常にシンプルなソリューション。他のコードは必要ありません。

public enum MyStatus
{
    Active = 1,
    Archived = 2
}

使用に関する文字列を取得するには:

MyStatus.Active.ToString("f");

または

MyStatus.Archived.ToString("f");`

値は「アクティブ」または「アーカイブ済み」になります。

呼び出し時にさまざまな文字列形式(上記の「f」)をEnum.ToString確認するには、この列挙形式文字列ページを参照してください。


28

System.ComponentModel名前空間のDescription属性を使用しています。単に列挙型を装飾してから、次のコードを使用してそれを取得します。

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)
                {
                    //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();

        }

例として:

public enum Cycle : int
{        
   [Description("Daily Cycle")]
   Daily = 1,
   Weekly,
   Monthly
}

このコードは、「フレンドリ名」を必要としない列挙型にうまく対応し、列挙型の.ToString()のみを返します。


27

私はJakubŠturcの答えが本当に好きですが、switch-caseステートメントでは使用できないという欠点があります。以下は、switchステートメントで使用できる、彼の回答を少し変更したバージョンです。

public sealed class AuthenticationMethod
{
    #region This code never needs to change.
    private readonly string _name;
    public readonly Values Value;

    private AuthenticationMethod(Values value, String name){
        this._name = name;
        this.Value = value;
    }

    public override String ToString(){
        return _name;
    }
    #endregion

    public enum Values
    {
        Forms = 1,
        Windows = 2,
        SSN = 3
    }

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}

したがって、JakubŠturcの答えのすべての利点が得られます。さらに、switchステートメントで次のように使用できます。

var authenticationMethodVariable = AuthenticationMethod.FORMS;  // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString();       // Get the user-friendly "name" of the "enum" value.

// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
    case authenticationMethodVariable.Values.Forms: // Do something
        break;
    case authenticationMethodVariable.Values.Windows: // Do something
        break;
    case authenticationMethodVariable.Values.SSN: // Do something
        break;      
}

より短い解決策は、列挙型{}を削除して、構築したEnumの数の静的なカウントを維持することです。これにより、作成した新しいインスタンスを列挙型リストに追加する必要がないという利点もあります。たとえばpublic static int nextAvailable { get; private set; }、コンストラクタでthis.Value = nextAvailable++;
kjhf

興味深いアイデア@kjhf。私の懸念は、誰かがコードを並べ替えると、列挙値に割り当てられた値も変わる可能性があることです。たとえば、これにより、列挙値がファイル/データベースに保存されたときに誤った列挙値が取得され、「新しいAuthenticationMethod(...)」行の順序が変更されます(たとえば、1つが削除されます)。アプリを再度実行し、ファイル/データベースから列挙値を取得します。列挙値は、最初に保存されたAuthenticationMethodと一致しない場合があります。
デッドリードッグ2014

良い点-これらの特定のケースでは、人々が列挙型の整数値(または列挙型コードの並べ替え)に依存しないことを願っていますが、この値は純粋にスイッチとして使用され、おそらく.Equals()および。 GetHashCode()。P:心配ならば、あなたは常に「DO NOT REORDER」を持つ巨大なコメント入れることができます
kjhf

=オペレーターをオーバーロードして、スイッチを機能させることはできませんか?これはVBで行い、select caseステートメントで使用できます。
user1318499 2014

@ user1318499いいえ。C#には、switchステートメントに関してVBよりも厳しいルールがあります。Caseステートメントにクラスインスタンスを使用することはできません。定数プリミティブのみを使用できます。
deadlydog

13

私は、いくつかのキャッシングと組み合わせて、上記の提案のいくつかを組み合わせて使用​​します。さて、ネット上のどこかで見つけたコードからアイデアを得ましたが、どこで手に入れたのか思い出せません。そのため、誰かが似たようなものを見つけた場合は、帰属をコメントしてください。

とにかく、使用にはタイプコンバーターが含まれるため、UIにバインドしている場合は「機能する」だけです。タイプコンバーターから静的メソッドに初期化することで、コードをすばやく検索できるようにJakubのパターンで拡張できます。

基本的な使用法は次のようになります

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
    // The custom type converter will use the description attribute
    [Description("A custom description")]
    ValueWithCustomDescription,

   // This will be exposed exactly.
   Exact
}

カスタム列挙型コンバーターのコードは次のとおりです。

public class CustomEnumTypeConverter<T> : EnumConverter
    where T : struct
{
    private static readonly Dictionary<T,string> s_toString = 
      new Dictionary<T, string>();

    private static readonly Dictionary<string, T> s_toValue = 
      new Dictionary<string, T>();

    private static bool s_isInitialized;

    static CustomEnumTypeConverter()
    {
        System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
          "The custom enum class must be used with an enum type.");
    }

    public CustomEnumTypeConverter() : base(typeof(T))
    {
        if (!s_isInitialized)
        {
            Initialize();
            s_isInitialized = true;
        }
    }

    protected void Initialize()
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            string description = GetDescription(item);
            s_toString[item] = description;
            s_toValue[description] = item;
        }
    }

    private static string GetDescription(T optionValue)
    {
        var optionDescription = optionValue.ToString();
        var optionInfo = typeof(T).GetField(optionDescription);
        if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
        {
            var attribute = 
              (DescriptionAttribute)Attribute.
                 GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
            return attribute.Description;
        }
        return optionDescription;
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, 
       object value, Type destinationType)
    {
        var optionValue = (T)value;

        if (destinationType == typeof(string) && 
            s_toString.ContainsKey(optionValue))
        {
            return s_toString[optionValue];
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, object value)
    {
        var stringValue = value as string;

        if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
        {
            return s_toValue[stringValue];
        }

        return base.ConvertFrom(context, culture, value);
    }
}

}


12

あなたの質問では、列挙型の数値が実際にどこでも必要であるとは決して言っていません。

string型のenumを必要としない場合(これは整数型ではないため、enumのベースにはなれません)、ここに方法があります。

    static class AuthenticationMethod
    {
        public static readonly string
            FORMS = "Forms",
            WINDOWSAUTHENTICATION = "WindowsAuthentication";
    }

enumと同じ構文を使用して参照できます

if (bla == AuthenticationMethod.FORMS)

数値の場合よりも少し遅くなりますが(数値の代わりに文字列を比較)、プラス側では、文字列へのアクセスにリフレクション(遅い)を使用していません。


「静的読み取り専用」の代わりに「const」を使用する場合、switchステートメントでケースラベルとして値を使用できます。
Ed N.

11

これを拡張メソッドとして解決した方法:

using System.ComponentModel;
public static string GetDescription(this Enum value)
{
    var descriptionAttribute = (DescriptionAttribute)value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(false)
        .Where(a => a is DescriptionAttribute)
        .FirstOrDefault();

    return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}

列挙型:

public enum OrderType
{
    None = 0,
    [Description("New Card")]
    NewCard = 1,
    [Description("Reload")]
    Refill = 2
}

使用法(o.OrderTypeは列挙型と同じ名前のプロパティです):

o.OrderType.GetDescription()

これにより、実際の列挙値NewCardおよびRefillの代わりに、「New Card」または「Reload」の文字列が得られます。


完全を期すために、DescriptionAttributeクラスのコピーを含める必要があります。
バーニーホワイト

3
バーニー、DescriptionAttributeはSystem.ComponentModelにあります
agentnega

11

更新: 8年後、長い間C#に触れなかった後、このページにアクセスすると、私の答えはもはや最善の解決策ではないようです。私は、属性関数に関連付けられたコンバーターソリューションが本当に好きです。

これを読んでいる場合は、他の回答も確認してください。
(ヒント:これらはこの上にあります)


皆さんと同じように、私はJakubŠturcの選択した回答を気に入っていましたが、コードをコピーして貼り付けることも嫌いで、できる限り少なくしようとしています。

そのため、ほとんどの機能が継承または組み込まれているEnumBaseクラスが必要であると判断し、動作ではなくコンテンツに集中できるようにしました。

このアプローチの主な問題は、Enum値はタイプセーフなインスタンスですが、相互作用はEnumクラスタイプのStatic実装と相互作用するという事実に基づいています。だからジェネリックマジックの少しの助けを借りて、私は最終的に正しいミックスを得たと思います。誰かがこれが私と同じくらい便利だと思うことを願っています。

まずJakubの例から始めますが、継承とジェネリックを使用します。

public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
    public static readonly AuthenticationMethod FORMS =
        new AuthenticationMethod(1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
        new AuthenticationMethod(2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON =
        new AuthenticationMethod(3, "SSN");

    private AuthenticationMethod(int Value, String Name)
        : base( Value, Name ) { }
    public new static IEnumerable<AuthenticationMethod> All
    { get { return EnumBase<AuthenticationMethod, int>.All; } }
    public static explicit operator AuthenticationMethod(string str)
    { return Parse(str); }
}

そして、これが基本クラスです:

using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call

// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
//   derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
    #region Instance code
    public T Value { get; private set; }
    public string Name { get; private set; }

    protected EnumBase(T EnumValue, string Name)
    {
        Value = EnumValue;
        this.Name = Name;
        mapping.Add(Name, this);
    }

    public override string ToString() { return Name; }
    #endregion

    #region Static tools
    static private readonly Dictionary<string, EnumBase<E, T>> mapping;
    static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
    protected static E Parse(string name)
    {
        EnumBase<E, T> result;
        if (mapping.TryGetValue(name, out result))
        {
            return (E)result;
        }

        throw new InvalidCastException();
    }
    // This is protected to force the child class to expose it's own static
    // method.
    // By recreating this static method at the derived class, static
    // initialization will be explicit, promising the mapping dictionary
    // will never be empty when this method is called.
    protected static IEnumerable<E> All
    { get { return mapping.Values.AsEnumerable().Cast<E>(); } }
    #endregion
}

ベースの静的コンストラクターから派生した静的コンストラクターを呼び出すことができる場合があります。私はまだそれに探していますが、今のところ、私はそれで何の問題は認められません:stackoverflow.com/questions/55290034/...
コーリー-G

10

キースに同意しますが、(まだ)投票できません。

静的メソッドとswithステートメントを使用して、必要なものを正確に返します。データベースにはtinyintを格納し、コードは実際の列挙型のみを使用するため、文字列はUI要件に対応しています。数多くのテストを行った結果、最高のパフォーマンスとほとんどの出力制御が得られました。

public static string ToSimpleString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "ComplexForms";
             break;
     }
}

public static string ToFormattedString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "Complex Forms";
             break;
     }
}

ただし、一部のアカウントでは、これにより、メンテナンスの悪夢とコードの臭いが発生する可能性があります。列挙型が長くて多数ある列挙型、または頻繁に変更される列挙型には注意してください。そうでなければ、これは私にとって素晴らしい解決策でした。


10

単純な「列挙型」を実装するためにここに来たが、その値が整数ではなく文字列である場合、最も簡単な解決策は次のとおりです。

    public sealed class MetricValueList
    {
        public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
        public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
    }

実装:

var someStringVariable = MetricValueList.Brand;

2
変数をconstsにする方が、を使用するよりも良いでしょうstatic readonly
AndyGeek

1
constsは、コンパイル時にベイク処理されるため、パブリックにアクセス可能なクラスには適していません。コード全体をconstsで再コンパイルせずにサードパーティDLLを置き換えることはできません。constsとstatic readonlyのパフォーマンスオフセットは無視できます。
クリスチャンウィリアムズ

7

この問題に直面したとき、最初に答えを見つけようとするいくつかの質問があります。

  • 私の列挙値の名前は、目的のために十分にわかりやすいですか、それともわかりやすい名前を提供する必要がありますか?
  • 往復する必要がありますか?つまり、テキスト値を取得してそれらを列挙値に解析する必要がありますか?
  • これは、プロジェクトの多くの列挙型に必要なものですか、それとも1つだけですか?
  • この情報を表示するのはどのようなUI要素ですか?特に、UIにバインドするのですか、それともプロパティシートを使用するのですか?
  • これはローカライズ可能である必要がありますか?

これを行う最も簡単な方法は、を使用することですEnum.GetValue(およびを使用してラウンドトリップをサポートしますEnum.Parse)。TypeConverterSteve Mitchamが示唆するように、UIバインディングをサポートするを構築することもしばしば価値があります。(ビルドする必要はありませんTypeConverterプロパティシートを使用している場合は、プロパティシートを使用ときにん。これは、プロパティシートの優れた点の1つです。ロードには独自の問題があることはわかっています。)

一般に、上記の質問への回答でうまくいかないことがわかった場合、次のステップは、静的Dictionary<MyEnum, string>、またはを作成して入力することDictionary<Type, Dictionary<int, string>>です。私は通常、次のパイクを降りてくるのは、デプロイ後にフレンドリーな値を変更する必要があるためです(多くの場合、ローカライズのために常にではありません)。


7

これをコメントとして以下に引用された投稿に投稿したかったのですが、十分な担当者がいないため投稿できませんでした。したがって、反対投票はしないでください。コードにエラーが含まれていたので、この解決策を使用しようとしている個人にこれを指摘したいと思います。

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,
  // This will be exposed exactly.
  Exact
}

する必要があります

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,

  // This will be exposed exactly.
  Exact
}

ブリリアント!


5

私の亜種

public struct Colors
{
    private String current;

    private static string red = "#ff0000";
    private static string green = "#00ff00";
    private static string blue = "#0000ff";

    private static IList<String> possibleColors; 

    public static Colors Red { get { return (Colors) red; } }
    public static Colors Green { get { return (Colors) green; } }
    public static Colors Blue { get { return (Colors) blue; } }

    static Colors()
    {
        possibleColors = new List<string>() {red, green, blue};
    }

    public static explicit operator String(Colors value)
    {
        return value.current;
    }

    public static explicit operator Colors(String value)
    {
        if (!possibleColors.Contains(value))
        {
            throw new InvalidCastException();
        }

        Colors color = new Colors();
        color.current = value;
        return color;
    }

    public static bool operator ==(Colors left, Colors right)
    {
        return left.current == right.current;
    }

    public static bool operator !=(Colors left, Colors right)
    {
        return left.current != right.current;
    }

    public bool Equals(Colors other)
    {
        return Equals(other.current, current);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != typeof(Colors)) return false;
        return Equals((Colors)obj);
    }

    public override int GetHashCode()
    {
        return (current != null ? current.GetHashCode() : 0);
    }

    public override string ToString()
    {
        return current;
    }
}

コードは少し見苦しく見えますが、この構造体の使用法はかなり見栄えがします。

Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000

Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00

// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error

Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True

Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False

また、そのような列挙型が多数必要な場合は、コード生成(T4など)が使用される可能性があると思います。


4

オプション1:

public sealed class FormsAuth
{
     public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
     public override string ToString{return "Windows Authtentication";}
}

public sealed class SsoAuth
{
     public override string ToString{return "SSO";}
}

その後

object auth = new SsoAuth(); //or whatever

//...
//...
// blablabla

DoSomethingWithTheAuth(auth.ToString());

オプション2:

public enum AuthenticationMethod
{
        FORMS = 1,
        WINDOWSAUTHENTICATION = 2,
        SINGLESIGNON = 3
}

public class MyClass
{
    private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
    public MyClass()
    {
         map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
         map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
         map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
    }
}

4

私たちが解決しようとしている問題について考えたら、それは私たちが必要としている列挙型ではありません。特定の数の値を相互に関連付けることができるオブジェクトが必要です。つまり、クラスを定義します。

JakubŠturcのタイプセーフなenumパターンは、私がここで見る最良のオプションです。

それを見ろ:

  • プライベートコンストラクターがあるため、クラス自体のみが許可された値を定義できます。
  • これはシールされたクラスなので、継承によって値を変更することはできません。
  • これはタイプセーフであり、メソッドはそのタイプのみを要求できます。
  • 値にアクセスすることによる反射パフォーマンスへの影響はありません。
  • そして最後に、名前、説明、数値など、3つ以上のフィールドを関連付けるように変更できます。

4

私にとって、実用的なアプローチはクラス内のクラスです、サンプル:

public class MSEModel
{
    class WITS
    {
        public const string DATE = "5005";
        public const string TIME = "5006";
        public const string MD = "5008";
        public const string ROP = "5075";
        public const string WOB = "5073";
        public const string RPM = "7001";
... 
    }

4

.NETで文字列値の列挙型を作成するための基本クラスを作成しました。これは、コピーしてプロジェクトに貼り付けたり、StringEnumという名前のNuGetパッケージを使用してインストールしたりできる1つのC#ファイルです。 GitHubリポジトリ

  • Intellisenseは、クラスにxmlコメントで注釈が付けられている場合、列挙名を提案し<completitionlist>ます。(C#とVBの両方で機能します)

Intellisenseデモ

  • 通常の列挙型と同様の使用法:
///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = Create("#FF0000");
    public static readonly HexColor Green = Create("#00FF00");
    public static readonly HexColor Red = Create("#000FF");
}
    // Static Parse Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true

    // Conversion from your `StringEnum` to `string`
    string myString1 = HexColor.Red.ToString(); // => "#FF0000"
    string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)

インストール:

  • 次のStringEnum基本クラスをプロジェクトに貼り付けます。(最新バージョン
  • または、> = 1.0、> = 4.5、> = 4.6などで動作するように基づいているStringEnum NuGetパッケージをインストールします。.Net Standard 1.0.Net Core.Net FrameworkMono
    /// <summary>
    /// Base class for creating string-valued enums in .NET.<br/>
    /// Provides static Parse() and TryParse() methods and implicit cast to string.
    /// </summary>
    /// <example> 
    /// <code>
    /// class Color : StringEnum &lt;Color&gt;
    /// {
    ///     public static readonly Color Blue = Create("Blue");
    ///     public static readonly Color Red = Create("Red");
    ///     public static readonly Color Green = Create("Green");
    /// }
    /// </code>
    /// </example>
    /// <typeparam name="T">The string-valued enum type. (i.e. class Color : StringEnum&lt;Color&gt;)</typeparam>
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static Dictionary<string, T> valueDict = new Dictionary<string, T>();
        protected static T Create(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueDict.Add(value, result);
            return result;
        }

        public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
        public override string ToString() => Value;

        public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
        public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;

        public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
        bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case and takes O(log n). False allows different case but is little bit slower (O(n))</param>
        public static T Parse(string value, bool caseSensitive = true)
        {
            var result = TryParse(value, caseSensitive);
            if (result == null)
                throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");

            return result;
        }

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case. False allows different case but is slower: O(n)</param>
        public static T TryParse(string value, bool caseSensitive = true)
        {
            if (value == null) return null;
            if (valueDict.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            if (caseSensitive)
            {
                if (valueDict.TryGetValue(value, out T item))
                    return item;
                else
                    return null;
            }
            else
            {
                // slower O(n) case insensitive search
                return valueDict.FirstOrDefault(f => f.Key.Equals(value, StringComparison.OrdinalIgnoreCase)).Value;
                // Why Ordinal? => https://esmithy.net/2007/10/15/why-stringcomparisonordinal-is-usually-the-right-choice/
            }
        }
    }

3

文字列を列挙型に関連付けるタスクを実行するさらに別の方法を次に示します。

struct DATABASE {
    public enum enums {NOTCONNECTED, CONNECTED, ERROR}
    static List<string> strings =
        new List<string>() {"Not Connected", "Connected", "Error"};

    public string GetString(DATABASE.enums value) {
        return strings[(int)value];
    }
}

このメソッドは次のように呼び出されます:

public FormMain() {
    DATABASE dbEnum;

    string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}

関連する列挙型を独自の構造体にグループ化できます。このメソッドは列挙型を使用するため、GetString()呼び出しを行うときにIntellisenseを使用して列挙型のリストを表示できます。

オプションで、DATABASE構造体でnew演算子を使用できます。これを使用しListないと、最初のGetString()呼び出しが行われるまで文字列は割り当てられません。


3

ここには多くの素晴らしい答えがありますが、私の場合、「文字列列挙型」から私が欲しかったものを解決できませんでした。

  1. switchステートメントで使用できます。例:switch(myEnum)
  2. foo(myEnum type)などの関数パラメーターで使用できます。
  3. 参照できる例:myEnum.FirstElement
  4. 文字列を使用できます。例:foo( "FirstElement")== foo(myEnum.FirstElement)

1、2、4は、実際には文字列のC#Typedefで解決できます(文字列はC#で切り替え可能であるため)

3は静的なconst文字列で解決できます。したがって、同じニーズがある場合、これが最も簡単なアプローチです。

public sealed class Types
{

    private readonly String name;

    private Types(String name)
    {
        this.name = name;

    }

    public override String ToString()
    {
        return name;
    }

    public static implicit operator Types(string str)
    {
        return new Types(str);

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }


    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";
    public const string Folder = "Folder";
    #endregion

}

これにより、たとえば次のことが可能になります。

    public TypeArgs(Types SelectedType)
    {
        Types SelectedType = SelectedType
    }

そして

public TypeObject CreateType(Types type)
    {
        switch (type)
        {

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

CreateTypeは、文字列またはタイプで呼び出すことができます。ただし、欠点は、任意の文字列が自動的に有効な列挙型になることです。になること。これは変更できますが、何らかのinit関数が必要になります。

これで、int値が重要な場合(おそらく比較速度のため)、JakubŠturcの素晴らしい答えからいくつかのアイデアを使用して、少しおかしなことを行うことができます。これが私の試みです。

    public sealed class Types
{
    private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
    private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();

    private readonly String name;
    private static int layerTypeCount = 0;
    private int value;
    private Types(String name)
    {
        this.name = name;
        value = layerTypeCount++;
        strInstance[name] = this;
        intInstance[value] = this;
    }

    public override String ToString()
    {
        return name;
    }


    public static implicit operator Types(int val)
    {
        Types result;
        if (intInstance.TryGetValue(val, out result))
            return result;
        else
            throw new InvalidCastException();
    }

    public static implicit operator Types(string str)
    {
        Types result;
        if (strInstance.TryGetValue(str, out result))
        {
            return result;
        }
        else
        {
            result = new Types(str);
            return result;
        }

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }

    public static bool operator ==(Types a, Types b)
    {
        return a.value == b.value;
    }
    public static bool operator !=(Types a, Types b)
    {
        return a.value != b.value;
    }

    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";

    #endregion

}

もちろん「タイプbob = 4;」最初にそれらを初期化しなければ、意味がなくなります。

しかし、理論的にはTypeA == TypeBの方が速いでしょう...


3

私が正しく理解している場合は、.ToString()を使用して、値から列挙型の名前を取得できます(列挙型がすでに列挙型としてキャストされている場合)。裸のint(データベースなどから)がある場合は、最初にそれを列挙型にキャストできます。以下のどちらの方法でも、列挙名が得られます。

AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS

ただし、2番目の手法では、intを使用していて、インデックスが1ベース(0ベースではない)であると想定しています。関数GetNamesも比較的重いです。呼び出されるたびに配列全体を生成しています。最初の手法でわかるように、.ToString()は実際には暗黙的に呼び出されます。もちろん、これらは両方とも回答ですでに言及されていますが、私はそれらの違いを明確にしようとしています。


3

古い投稿ですが...

これに対する答えは実際には非常に簡単かもしれません。Enum.ToString()関数を使用する

この関数には6つのオーバーロードがあり、Enum.Tostring( "F")またはEnum.ToString()を使用して文字列値を返すことができます。他のことを気にする必要はありません。これが実際のデモです

このソリューションはすべてのコンパイラで機能するとは限りませんこのデモは期待どおりに機能しません)が、少なくとも最新のコンパイラでは機能することに注意してください。



2

さて、上記のすべてを読んだ後、私は連中が列挙子を文字列に変換する問題を複雑にしているように感じます。列挙型フィールドに属性を付けるというアイデアは気に入りましたが、属性は主にメタデータに使用されると思いますが、あなたの場合、必要なのはある種のローカリゼーションだけだと思います。

public enum Color 
{ Red = 1, Green = 2, Blue = 3}


public static EnumUtils 
{
   public static string GetEnumResourceString(object enumValue)
    {
        Type enumType = enumValue.GetType();
        string value = Enum.GetName(enumValue.GetType(), enumValue);
        string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
        string result = Resources.Enums.ResourceManager.GetString(resourceKey);
        if (string.IsNullOrEmpty(result))
        {
            result = String.Format("{0}", value);
        }
        return result;
    }
}

上記のメソッドを呼び出そうとすると、この方法で呼び出すことができます

public void Foo()
{
  var col = Color.Red;
  Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}

必要なのは、すべての列挙子の値と対応する文字列を含むリソースファイルを作成することだけです。

リソース名リソース値
Color_Redマイストリングの色は赤
Color_Blueブルーイー
Color_Greenハルクカラー

これについて実際に非常に優れているのは、アプリケーションをローカライズする必要がある場合に非常に役立つということです。必要なことは、新しい言語で別のリソースファイルを作成することだけです。とボーラ!


1

私がそのような状況にあるとき、私は以下の解決策を提案します。

そして、あなたが持つことができる消費クラスとして

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

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}

そして、双方向ディクショナリの使用:これに基づいて(https://stackoverflow.com/a/255638/986160)キーがディクショナリ内の単一の値に関連付けられ、(https://stackoverflow.com/a / 255630/986160)が少しエレガントです。このディクショナリも列挙可能であり、整数から文字列へと前後に移動できます。また、このクラスを除いて、コードベースに文字列を含める必要はありません。

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

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

1

より大きな文字列列挙セットの場合、リストされている例は面倒になる可能性があります。ステータスコードのリスト、または他の文字列ベースの列挙のリストが必要な場合、属性システムを使用するのは面倒であり、それ自体のインスタンスを持つ静的クラスを構成するのは面倒です。私自身のソリューションでは、T4テンプレートを利用して、文字列をサポートする列挙型を簡単に作成できるようにしています。結果は、HttpMethodクラスがどのように機能するかに似ています。

次のように使用できます。

    string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when needed
    ResponseStatusCode codeByValueOf = ResponseStatusCode.ValueOf(statusCode); // Returns null if not found

    // Implements TypeConverter so you can use it with string conversion methods.
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(ResponseStatusCode));
    ResponseStatusCode code = (ResponseStatusCode) converter.ConvertFromInvariantString(statusCode);

    // You can get a full list of the values
    bool canIterateOverValues = ResponseStatusCode.Values.Any(); 

    // Comparisons are by value of the "Name" property. Not by memory pointer location.
    bool implementsByValueEqualsEqualsOperator = "SUCCESS" == ResponseStatusCode.SUCCESS; 

Enum.ttファイルから始めます。

<#@ include file="StringEnum.ttinclude" #>


<#+
public static class Configuration
{
    public static readonly string Namespace = "YourName.Space";
    public static readonly string EnumName = "ResponseStatusCode";
    public static readonly bool IncludeComments = true;

    public static readonly object Nodes = new
    {
        SUCCESS = "The response was successful.",
        NON_SUCCESS = "The request was not successful.",
        RESOURCE_IS_DISCONTINUED = "The resource requested has been discontinued and can no longer be accessed."
    };
}
#>

次に、StringEnum.ttincludeファイルを追加します。

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;

namespace <#= Configuration.Namespace #>
{
    /// <summary>
    /// TypeConverter implementations allow you to use features like string.ToNullable(T).
    /// </summary>
    public class <#= Configuration.EnumName #>TypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var casted = value as string;

            if (casted != null)
            {
                var result = <#= Configuration.EnumName #>.ValueOf(casted);
                if (result != null)
                {
                    return result;
                }
            }

            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            var casted = value as <#= Configuration.EnumName #>;
            if (casted != null && destinationType == typeof(string))
            {
                return casted.ToString();
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

    [TypeConverter(typeof(<#= Configuration.EnumName #>TypeConverter))]
    public class <#= Configuration.EnumName #> : IEquatable<<#= Configuration.EnumName #>>
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T
//---------------------------------------------------------------------------------------------------
<# Write(Helpers.PrintEnumProperties(Configuration.Nodes)); #>

        private static List<<#= Configuration.EnumName #>> _list { get; set; } = null;
        public static List<<#= Configuration.EnumName #>> ToList()
        {
            if (_list == null)
            {
                _list = typeof(<#= Configuration.EnumName #>).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(<#= Configuration.EnumName #>))
                    .Select(x => x.GetValue(null)).OfType<<#= Configuration.EnumName #>>().ToList();
            }

            return _list;
        }

        public static List<<#= Configuration.EnumName #>> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static <#= Configuration.EnumName #> ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N
//---------------------------------------------------------------------------------------------------      
        public string Name { get; private set; }
        public string Description { get; private set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(<#= Configuration.EnumName #> d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(<#= Configuration.EnumName #> other)
        {
            return this.ToString() == other?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}

<#+

public static class Helpers
{
        public static string PrintEnumProperties(object nodes)
        {
            string o = "";
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props = nodesTp.GetProperties().OrderBy(p => p.Name).ToArray();

            for(int i = 0; i < props.Length; i++)
            {
                var prop = props[i];
                if (Configuration.IncludeComments)
                {
                    o += "\r\n\r\n";
                    o += "\r\n        ///<summary>";
                    o += "\r\n        /// "+Helpers.PrintPropertyValue(prop, Configuration.Nodes);
                    o += "\r\n        ///</summary>";
                }

                o += "\r\n        public static readonly "+Configuration.EnumName+" "+prop.Name+ " = new "+Configuration.EnumName+"(){ Name = \""+prop.Name+"\", Description = "+Helpers.PrintPropertyValue(prop, Configuration.Nodes)+ "};";
            }

            o += "\r\n\r\n";

            return o;
        }

        private static Dictionary<string, string> GetValuesMap()
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            var dic = new Dictionary<string,string>();
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                dic[prop.Name] = prop.GetValue(Configuration.Nodes).ToString();
            }
            return dic;
        }

        public static string PrintMasterValuesMap(object nodes)
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            string o = "        private static readonly Dictionary<string, string> ValuesMap = new Dictionary<string, string>()\r\n        {";
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                o += "\r\n            { \""+prop.Name+"\", "+(Helpers.PrintPropertyValue(prop,Configuration.Nodes)+" },");
            }
            o += ("\r\n        };\r\n");

            return o;
        }


        public static string PrintPropertyValue(PropertyInfo prop, object objInstance)
        {
            switch(prop.PropertyType.ToString()){
                case "System.Double":
                    return prop.GetValue(objInstance).ToString()+"D";
                case "System.Float":
                    return prop.GetValue(objInstance).ToString()+"F";
                case "System.Decimal":
                    return prop.GetValue(objInstance).ToString()+"M";
                case "System.Long":
                    return prop.GetValue(objInstance).ToString()+"L";
                case "System.Boolean":
                case "System.Int16":
                case "System.Int32":
                    return prop.GetValue(objInstance).ToString().ToLowerInvariant();
                case "System.String":
                    return "\""+prop.GetValue(objInstance)+"\"";
            }

            return prop.GetValue(objInstance).ToString();
        }

        public static string _ (int numSpaces)
        {
            string o = "";
            for(int i = 0; i < numSpaces; i++){
                o += " ";
            }

            return o;
        }
}
#>

最後に、Enum.ttファイルを再コンパイルすると、出力は次のようになります。

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;

namespace YourName.Space
{
    public class ResponseStatusCode
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T 
//---------------------------------------------------------------------------------------------------



        ///<summary>
        /// "The response was successful."
        ///</summary>
        public static readonly ResponseStatusCode SUCCESS = new ResponseStatusCode(){ Name = "SUCCESS", Description = "The response was successful."};


        ///<summary>
        /// "The request was not successful."
        ///</summary>
        public static readonly ResponseStatusCode NON_SUCCESS = new ResponseStatusCode(){ Name = "NON_SUCCESS", Description = "The request was not successful."};


        ///<summary>
        /// "The resource requested has been discontinued and can no longer be accessed."
        ///</summary>
        public static readonly ResponseStatusCode RESOURCE_IS_DISCONTINUED = new ResponseStatusCode(){ Name = "RESOURCE_IS_DISCONTINUED", Description = "The resource requested has been discontinued and can no longer be accessed."};


        private static List<ResponseStatusCode> _list { get; set; } = null;
        public static List<ResponseStatusCode> ToList()
        {
            if (_list == null)
            {
                _list = typeof(ResponseStatusCode).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(ResponseStatusCode))
                    .Select(x => x.GetValue(null)).OfType<ResponseStatusCode>().ToList();
            }

            return _list;
        }

        public static List<ResponseStatusCode> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static ResponseStatusCode ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N 
//---------------------------------------------------------------------------------------------------       
        public string Name { get; set; }
        public string Description { get; set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(ResponseStatusCode d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(ResponseStatusCode a, ResponseStatusCode b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(ResponseStatusCode a, ResponseStatusCode b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.