C#での列挙型と文字列の関連付け


363

Enumerationの型はintでなければならないため、以下は不可能であることを知っています

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}

私のデータベースからは、包括的なコード(OEMおよびCMB s)を。私はこの分野をenum何か他の理解できるものにしたいと思います。ターゲットが可読性である場合、ソリューションは簡潔である必要があるためです。

他にどのようなオプションがありますか?



12
ほとんどの回答が「const string」を使用しないのはなぜかはわかりませんが、代わりにカスタムクラスを作成しています。
CTS_AE 2015年

1
文字列は使用できない場合がありますが、charsは問題なく使用できます。これは、1文字の値を使用できる場合のオプションです。
T. Sar

1
CTS_AEによって上記で提案されたソリューションが上位3つの回答にさえない理由について、本当の意味で混乱しています。
シンジャイ

@Sinjai関連する値を明示的にグループ化すると、特にAPIや再利用可能なコンポーネントで、わずかなパフォーマンス低下のペナルティを上回ります。
person27

回答:


404

列挙型のように見えるので、メソッドではなくクラスでプロパティを使用するのが好きです。

ロガーの例を次に示します。

public class LogCategory
{
    private LogCategory(string value) { Value = value; }

    public string Value { get; set; }

    public static LogCategory Trace   { get { return new LogCategory("Trace"); } }
    public static LogCategory Debug   { get { return new LogCategory("Debug"); } }
    public static LogCategory Info    { get { return new LogCategory("Info"); } }
    public static LogCategory Warning { get { return new LogCategory("Warning"); } }
    public static LogCategory Error   { get { return new LogCategory("Error"); } }
}

タイプセーフな文字列値をパラメーターとして渡します。

public static void Write(string message, LogCategory logCategory)
{
    var log = new LogEntry { Message = message };
    Logger.Write(log, logCategory.Value);
}

使用法:

Logger.Write("This is almost like an enum.", LogCategory.Info);

4
私が思いつくことができる唯一の欠点は、それが少し遅いだろうということですが、これはほとんどの場合無視できます。また、エディターでの動作はまったく同じではありません。EG:これを切り替えても、可能性ごとにケースが自動的に入力されるわけではありません。これらのマイナーな点を除いて、これはおそらくかなり簡単な解決策だと思います。
Boris Callens、

3
また、Dictionary <LogCategory、Action / Func>をスイッチとして使用するのは簡単です。:)
Arnis Lapsa 09

4
@ArnisL。キーとして機能するだけでは不十分です。Equals()とGetHashCode()をオーバーライドする必要があり、Valueプロパティセッターをプライベートにします。それでも、それは列挙型ではありません。
Dave Van den Eynde 2013

21
私自身が使用するために、この概念を拡張し、ToStringメソッドをオーバーライドしてreturnを返しましたValue。そして、文字列への暗黙のキャスト演算子を提供しました。public static implicit operator String(LogCategory category) { return Value; }
ザレフェス2014年

6
スイッチケースでこれを使用するのはどうですか?
デビッド

175

拡張モデルを使用することもできます。

public enum MyEnum
{
    [Description("String 1")]
    V1= 1,
    [Description("String 2")]
    V2= 2
} 

あなたの拡張クラス

public static class MyEnumExtensions
{
    public static string ToDescriptionString(this MyEnum val)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])val
           .GetType()
           .GetField(val.ToString())
           .GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : string.Empty;
    }
} 

使用法:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());

3
説明として、別の拡張機能と文字列から列挙型については、stackoverflow.com / questions / 4367723 /…も参照してください。
デイブ

15
テキストを表示するたびに列挙型を反映することは、パフォーマンスの観点からはちょっと痛い音だと思わずにはいられません!
Liath、2014

4
@Liath-`.ToString()`はすでにリフレクションを使用しているため、このアプローチで何も失うことはなく、読みやすさも向上します
James King

1
すべての列挙型に自動的に適用されるように、拡張機能をジェネリックにできますか?
erosebe

3
汎用的にするには、public static string ToDescriptionString(this Enum ...明示的にに入力せずにieを使用しMyEnumます。
LeeCambl 2018

100

定数で静的クラスを使用するのはどうですか?

static class GroupTypes
{
  public const string TheGroup = "OEM";
  public const string TheOtherGroup = "CMB";
}

void DoSomething(string groupType)
{
  if(groupType == GroupTypes.TheGroup)
  {
    // Be nice
  }  
  else if (groupType == GroupTypes.TheOtherGroup)
  {
    // Continue to be nice
  }
  else
  {
    // unexpected, throw exception?
  }
}

9
同意した。結果の「列挙型」を切り替えることができる場合を除いて、より複雑なソリューションの目的を理解するのに苦労しています。
2011

@fakeleftジェネリック型(テンプレート)で静的クラス型を使用することはできません。おそらく他の制限もあります。そのため、人々は「より複雑な」ソリューションを好むと思います。
eselk 2015年

2
これを機能させるには、定数を内部またはパブリックにする必要があります
arviman '30

46
静的型はパラメーターとして使用できません。
ペドロモレイラ

2
@PedroMoreiraが指摘するように、GroupTypes静的クラスであるため、引数型として渡すことはできません。それがミエンの答えでさえ解決する問題です。この場合、あなたの代わりに持っている必要があるだろうvoid DoSomething(string groupType)し、その手段groupTypeかもしれないいかなる文字列値を使用すると、これらの無効なタイプのために準備する必要があり、それらをどのように処理するかを決める手段としても価値は、あなたが期待されていないことを、(たとえば、例外をスローすることによって)。Mienの答えでさえ、有効な入力の数をLogCategoryクラスで定義されたオプションに制限することでそれを解決します。
Pharap

30

列挙型の項目に属性を追加し、リフレクションを使用して属性から値を取得できます。

次のように、「フィールド」指定子を使用して属性を適用する必要があります。

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}

次に、列挙型の静的フィールド(この場合はGroupTypes)をリフレクトし、DescriptionAttributeリフレクションを使用して探していた値のを取得します。

public static DescriptionAttribute GetEnumDescriptionAttribute<T>(
    this T value) where T : struct
{
    // The type of the enum, it will be reused.
    Type type = typeof(T);

    // If T is not an enum, get out.
    if (!type.IsEnum) 
        throw new InvalidOperationException(
            "The type parameter T must be an enum type.");

    // If the value isn't defined throw an exception.
    if (!Enum.IsDefined(type, value))
        throw new InvalidEnumArgumentException(
            "value", Convert.ToInt32(value), type);

    // Get the static field for the value.
    FieldInfo fi = type.GetField(value.ToString(), 
        BindingFlags.Static | BindingFlags.Public);

    // Get the description attribute, if there is one.
    return fi.GetCustomAttributes(typeof(DescriptionAttribute), true).
        Cast<DescriptionAttribute>().SingleOrDefault();
}

私は返すことを選びました DescriptionAttribute属性が適用されているかどうかを判断できるようにしたい場合は、上記それ自体ました。


もっと複雑な状況ではこれを覚えていますが、OPで述べた複雑さのレベルの状況ではかなり複雑です
Boris Callens 2012

26

実際にはとても簡単にできます。次のコードを使用します。

enum GroupTypes
{
   OEM,
   CMB
};

次に、各列挙型要素の文字列値を取得する場合は、次のコード行を使用します。

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

私は過去にこのメソッドをうまく使用しており、定数クラスを使用して文字列定数を保持したこともありますが、どちらもかなりうまくいきますが、私はこれを好む傾向があります。


私は同じことを考えていましたが、これにはいくつかの落とし穴があるはずです...さもなければ、もっと多くの人がこれを提案するのではないかと思います(たぶん私は単なる偏執狂です)。
Matthijs Wessels、2010年

私がこれについて知っている唯一のキャッチは、ストリングを理解するためにリフレクションを使用していると私が信じていることです。結果として、定数文字列を追跡するソリューションの直後であれば、通常、クラスを使用して定数文字列の大部分を格納します。ただし、Enumが適切な解決策である場合(Enum要素に関する説明の文字列を取得するかどうかに関係なく)、管理するためにどこかに浮かぶ余分な文字列を用意するのではなく、説明したように列挙値を使用します。
アーサーC

1これが最良かつ最も簡単な答えであり、また高い票を持って、ここでそれを証明します。拡張モデルを使用する方が良いのは、テキストにスペースが必要な場合のみです(詳細はこちら)。
SharpC 2013

14
いいえ、これは列挙値の名前を取得するだけであり、文字列を列挙値に割り当てません。OPの目的は、列挙値とは異なる文字列を持つことです。たとえば、TheGroup = "OEM"、TheOtherGroup = "CMB"です。
Tim Autin、2014年

3
私は、これは、ティムさんのコメント@に同意していない OPがやろうとしているもの。この使用例が何であるか疑問に思っている場合は、デバイスが文字列をコマンドとして受け取る状況を考慮してください。ただし、コマンドの「人間が読める」バージョンも必要です。「Update Firmware」などのコマンドを「UPDATEFW」コマンドに関連付けるには、これが必要でした。
JYelton、2015年

20

静的クラスに定数を追加してみてください。Typeで終わるわけではありませんが、読みやすく整理された定数があります。

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}

3
コードからわかりやすい名前に戻すのが難しい。一致を検索するには、すべてのconstフィールドに対してリフレクションを使用する必要があります。
andleer 2009年

1
@andleer私はあなたの懸念を理解していません。これは私が使用するソリューションです。
VSO、

ええ、これはまさに私が欲しかったものです。そしてこれは、int値を使用して列挙型を定義しているかのように、最も簡潔でエレガントなソリューションですが、代わりに文字列値を使用しています。100%完璧です。
チャド

3
これの問題は、有限の値のリストを持つ別の型がないという意味で、それがEnumとして機能しないことです。これらを想定している関数は、エラーが発生しやすい自由形式の文字列で使用できます。
フアンマルティネス

14

以下を含むDB用に2つ目の列挙型を作成します。

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

これで、Enum.Parseを使用して、文字列 "OEM"および "CMB"から正しいDBGroupTypes値を取得できます。次に、それらをintに変換し、モデルでさらに使用したい正しい列挙から正しい値を取得できます。


これはプロセスの追加のステップのようです、なぜすべてを処理する1つのクラスでないのですか?
C.ロス

11
属性とリフレクションを使用するのではなく、
Dave Van den Eynde 2009年

13

クラスを使用します。

編集:より良い例

class StarshipType
{
    private string _Name;
    private static List<StarshipType> _StarshipTypes = new List<StarshipType>();

    public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
    public static readonly StarshipType Light = new StarshipType("Light");
    public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
    public static readonly StarshipType Heavy = new StarshipType("Heavy");
    public static readonly StarshipType Superheavy = new StarshipType("Superheavy");

    public string Name
    {
        get { return _Name; }
        private set { _Name = value; }
    }

    public static IList<StarshipType> StarshipTypes
    {
        get { return _StarshipTypes; }
    }

    private StarshipType(string name, int systemRatio)
    {
        Name = name;
        _StarshipTypes.Add(this);
    }

    public static StarshipType Parse(string toParse)
    {
        foreach (StarshipType s in StarshipTypes)
        {
            if (toParse == s.Name)
                return s;
        }
        throw new FormatException("Could not parse string.");
    }
}

1
コードからわかりやすい名前に戻すのが難しい。一致を検索するには、すべてのconstフィールドに対してリフレクションを使用する必要があります。
andleer 2009年

1
あなたの言ってる事がわかります。後で実際に動作するバージョンをアップロードしますが、かなり重いことは認めます。
C.ロス

C.ロスのソリューションに基づく私のバージョンstackoverflow.com/a/48441114/3862615
Roman M

7

この問題に対処するもう1つの方法は、列挙値と文字列の配列を使用して、列挙値を文字列のリストにマッピングすることです。

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};

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

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);

それはCMBを促します

長所:

  1. 簡単でクリーンなコード。
  2. 高パフォーマンス(特に、クラスを使用するアプローチとの比較)

短所:

  1. リストを編集するときはリストをめちゃくちゃにする傾向がありますが、短いリストでもかまいません。

6

これは、列挙値を文字列として取得するために使用した拡張メソッドです。まずここに列挙型があります。

public enum DatabaseEnvironment
{
    [Description("AzamSharpBlogDevDatabase")]
    Development = 1, 
    [Description("AzamSharpBlogQADatabase")]
    QualityAssurance = 2, 
    [Description("AzamSharpBlogTestDatabase")] 
    Test = 3
}

Description属性はSystem.ComponentModelから取得されました。

そして、これが私の拡張メソッドです:

public static string GetValueAsString(this DatabaseEnvironment environment) 
{
    // get the field 
    var field = environment.GetType().GetField(environment.ToString());
    var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);

    if(customAttributes.Length > 0)
    {
        return (customAttributes[0] as DescriptionAttribute).Description;  
    }
    else
    {
        return environment.ToString(); 
    }
}

これで、次のコードを使用して、enumに文字列値としてアクセスできます。

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}

5

辞書を使用してルックアップテーブルを検討しましたか?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);

次に、GroupTypeLookup.TryGetValue()を使用して、文字列を読み取ったときに文字列を検索できます。


特定の値のキーを簡単に取得するにはどうすればよいですか?
eglasius 2009年

質問は逆に行くことを求めていませんでした。しかし、逆に進む別の辞書を作成するのは十分簡単です。つまり、Dictionary <GroupTypes、string>です。
ジムミシェル

4
public class DataType
{
    private readonly string value;
    private static readonly Dictionary<string, DataType> predefinedValues;

    public static readonly DataType Json = new DataType("json");
    public static readonly DataType Xml = new DataType("xml");
    public static readonly DataType Text = new DataType("text");
    public static readonly DataType Html = new DataType("html");
    public static readonly DataType Binary = new DataType("binary");

    static DataType()
    {
        predefinedValues = new Dictionary<string, DataType>();
        predefinedValues.Add(Json.Value, Json);
        predefinedValues.Add(Xml.Value, Xml);
        predefinedValues.Add(Text.Value, Text);
        predefinedValues.Add(Html.Value, Html);
        predefinedValues.Add(Binary.Value, Binary);
    }

    private DataType(string value)
    {
        this.value = value;
    }

    public static DataType Parse(string value)
    {
        var exception = new FormatException($"Invalid value for type {nameof(DataType)}");
        if (string.IsNullOrEmpty(value))
            throw exception;

        string key = value.ToLower();
        if (!predefinedValues.ContainsKey(key))
            throw exception;

        return predefinedValues[key];
    }

    public string Value
    {
        get { return value; }
    }
}

3

C#は列挙された文字列をサポートしていませんが、ほとんどの状況では、リストまたは辞書を使用して目的の効果を得ることができます。

たとえば、合否結果を出力するには:

List<string> PassFail = new List<string> { "FAIL", "PASS" };
bool result = true;
Console.WriteLine("Test1: " + PassFail[result.GetHashCode()]);

2

私はそれをクラスにして、列挙型を完全に回避します。そして、typehandlerを使用すると、dbからオブジェクトを取得するときにオブジェクトを作成できます。

IE:

public class Group
{
    public string Value{ get; set; }
    public Group( string value ){ Value = value; } 
    public static Group TheGroup() { return new Group("OEM"); }
    public static Group OtherGroup() { return new Group("CMB"); }

}

2

辞書を作成して、コードをキーとして使用します。

編集:逆引き(キーの検索)の実行に関するコメントに対処するには、これは非常に効率的ではありません。これが必要な場合は、それを処理する新しいクラスを作成します。


また、特定の値のキーを簡単に取得できますか?
eglasius 2009年

C.ロスへ-どういう意味か分かりません。dbから値を読み取り、辞書に動的に入力できます。
jhale、2009年

2

私の最初の質問-データベース自体にアクセスできますか?これはデータベースで正規化する必要があります。そうしないと、ソリューションがエラーを起こしやすくなります。私の経験では、「OEM」と「CMB」でいっぱいのデータフィールドは、「oem」と他の「がらくたデータ」のようなものが時間の経過とともに混合される傾向があります。...正規化できれば、キーを使用できますEnumとして要素を含むテーブルで、これで完了です。構造はよりすっきりしています。

それが利用できない場合は、Enumを作成し、文字列を解析してEnumに変換するクラスを作成します。これにより、少なくとも非標準エントリの処理にある程度の柔軟性が提供され、Enum.Parse / Reflection / etcを使用して回避策を実行するよりもエラーをトラップまたは処理する際の柔軟性が大幅に向上します。辞書は機能しますが、ケースの問題などが発生した場合に故障する可能性があります。

あなたができるようにクラスを書くことをお勧めします:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

これにより、DBを変更しなくても、ほとんどの可読性が維持されます。


2

私が正しく理解している場合は、文字列から列挙型への変換が必要です。

enum GroupTypes {
    Unknown = 0,
    OEM = 1,
    CMB = 2
}
static GroupTypes StrToEnum(string str){
    GroupTypes g = GroupTypes.Unknown;
    try {
        object o = Enum.Parse(typeof(GroupTypes), str, true);
        g = (GroupTypes)(o ?? 0);
    } catch {
    }
    return g;
}
// then use it like this
GroupTypes g1 = StrToEnum("OEM");
GroupTypes g2 = StrToEnum("bad value");

必要に応じて、enum型のジェネリックスを使用して、より豪華にすることができます。


2

VS 2015では、nameofを使用できます

public class LogCategory
{
    public static string Trace;
    public static string Debug;
    public static string Info;
    public static string Warning;
    public static string Error;
}

使用法:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));

2

Glennular拡張メソッドを少し調整したので、ENUM以外のものにも拡張を使用できます。

using System;
using System.ComponentModel;
namespace Extensions {
    public static class T_Extensions {
        /// <summary>
        /// Gets the Description Attribute Value
        /// </summary>
        /// <typeparam name="T">Entity Type</typeparam>
        /// <param name="val">Variable</param>
        /// <returns>The value of the Description Attribute or an Empty String</returns>
        public static string Description<T>(this T t) {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])t.GetType().GetField(t.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }
    }
}

またはLinqを使用する

using System;
using System.ComponentModel;
using System.Linq;

namespace Extensions {


public static class T_Extensions {
        public static string Description<T>(this T t) =>
            ((DescriptionAttribute[])t
            ?.GetType()
            ?.GetField(t?.ToString())
            ?.GetCustomAttributes(typeof(DescriptionAttribute), false))
            ?.Select(a => a?.Description)
            ?.FirstOrDefault() 
            ?? string.Empty;  
    }
}

2

@Even Mienの回答に続いて、私はもう少し進んでGenericにしようとしましたが、ほとんどそこにあるようですが、1つのケースはまだ抵抗し、おそらくコードを少し単純化できます。
文字列から割り当てることができないため、私がどのように改善できるかを見て、特にそれを機能させることができる場合は、ここに投稿します

これまでのところ、私は次の結果を得ました:

        Console.WriteLine(TestEnum.Test1);//displays "TEST1"

        bool test = "TEST1" == TestEnum.Test1; //true

        var test2 = TestEnum.Test1; //is TestEnum and has value

        string test3 = TestEnum.Test1; //test3 = "TEST1"

        var test4 = TestEnum.Test1 == TestEnum.Test2; //false
         EnumType<TestEnum> test5 = "TEST1"; //works fine

        //TestEnum test5 = "string"; DOESN'T compile .... :(:(

魔法が起こる場所:

public abstract  class EnumType<T>  where T : EnumType<T>   
{

    public  string Value { get; set; }

    protected EnumType(string value)
    {
        Value = value;
    }


    public static implicit operator EnumType<T>(string s)
    {
        if (All.Any(dt => dt.Value == s))
        {
            Type t = typeof(T);

            ConstructorInfo ci = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,null, new Type[] { typeof(string) }, null);

            return (T)ci.Invoke(new object[] {s});
        }
        else
        {
            return null;
        }
    }

    public static implicit operator string(EnumType<T> dt)
    {
        return dt?.Value;
    }


    public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
    {
        return (string)ct1 == (string)ct2;
    }

    public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
    {
        return !(ct1 == ct2);
    }


    public override bool Equals(object obj)
    {
        try
        {
            return (string)obj == Value;
        }
        catch
        {
            return false;
        }
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public static IEnumerable<T> All
     => typeof(T).GetProperties()
       .Where(p => p.PropertyType == typeof(T))
       .Select(x => (T)x.GetValue(null, null));



}

私はそれから私の列挙のためにこれを宣言する必要があります:

public class TestEnum : EnumType<TestEnum> 
{

    private TestEnum(string value) : base(value)
    {}

    public static TestEnum Test1 { get { return new TestEnum("TEST1"); } }
    public static TestEnum Test2 { get { return new TestEnum("TEST2"); } }
}

この素晴らしい仕事をありがとう、私は長い間そのようなアプローチを探していました。これには1000ポイントが必要だと思います
user3492977

ああ、このコメントをありがとう、そしてこれを思い出してくれてありがとう。私がこのコードを書いたとき、私は2年間c#を使用していなかったので、すぐに戻ってきます。
ロミトラニ

@ user3492977 I最終的に戻ってそれを持って、それは完全に、私はまだ疑わしいfunctionnal午前作られた、それは素晴らしいアイデアや役に立たないことだ場合けれども:D stackoverflow.com/questions/62043138/...
Lomithrani

2

ネットコア3.0 / C#8.0(作業環境は、あなたのプロジェクトをアップグレードすることを可能にする場合)ショートハンドスイッチ文があることルックスやや列挙っぽいです。結局のところ、これは何年にもわたって使用してきた古い退屈なswitchステートメントと同じです。

ここでの唯一の本当の違いは、switchステートメントが新しいスーツを得たことです。

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

ここからコピーした上記のコードは、実際には列挙型をパラメーターとして使用しています。

それはあなたが望むものではありません(そして信頼してください、私は長い間OPが要求しているものに似たものを望んでいました)が、これは実際にはMSからのオリーブのブランチのようなものだと感じています。JMO。

それが誰かを助けることを願っています!


2

私は前の回答でほのめかした構造を使用しましたが、複雑さは排除しました。私にとって、これは文字列の列挙を作成することに最も似ていました。列挙が使用されるのと同じ方法で使用されます。

    struct ViewTypes
    {
        public const string View1 = "Whatever string you like";
        public const string View2 = "another string";
    }

使用例:

   switch( some_string_variable )
   {
      case ViewTypes.View1: /* do something */ break;
      case ViewTypes.View2: /* do something else */ break;
   }

1

@Even(経由で示唆したように私もいくつか列挙型を実装class Xし、public static Xちょうどこの頃、ネット4.5開始、がありますことを、後で調べるために、メンバー) ToString()方法があります。

今、私はすべてを列挙型に再実装しています。


1

これは、厳密に型指定されたパラメーターまたは文字列として使用する方法です。

public class ClassLikeEnum
{
    public string Value
    {
        get;
        private set;
    }

    ClassLikeEnum(string value) 
    {
        Value = value;
    }

    public static implicit operator string(ClassLikeEnum c)
    {
        return c.Value;
    }

    public static readonly ClassLikeEnum C1 = new ClassLikeEnum("RandomString1");
    public static readonly ClassLikeEnum C2 = new ClassLikeEnum("RandomString2");
}

1

2つの列挙型を使用できます。1つはデータベース用、もう1つは可読性用です。

あなたは彼らが同期していることを確認する必要があるだけです、それは小さなコストのようです。値を設定する必要はなく、位置を同じに設定するだけですが、値を設定すると、2つの列挙型が関連していることが明確になり、列挙型メンバーの再配置によるエラーが防止されます。また、コメントにより、メンテナンスクルーはこれらが関連しており、同期を保つ必要があることを知ることができます。

// keep in sync with GroupTypes
public enum GroupTypeCodes
{
    OEM,
    CMB
}

// keep in sync with GroupTypesCodes
public enum GroupTypes
{
    TheGroup = GroupTypeCodes.OEM,
    TheOtherGroup = GroupTypeCodes.CMB
}

それを使用するには、まずコードに変換するだけです。

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString();

次に、さらに便利にしたい場合は、このタイプの列挙型でのみ機能する拡張関数を追加できます。

public static string ToString(this GroupTypes source)
{
    return ((GroupTypeCodes)source).ToString();
}

そして、あなたはただ行うことができます:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();

これは悪い習慣です。依存関係があるenum場合、一方の意図した値の変更は、もう一方を誤って混乱させる可能性があります。
Lorenz Lo Sauer

1

私は基本的に@ArthurCによる反射の答えを探していました

彼の答えを少し拡張するために、ジェネリック関数を使用することでさらに良くすることができます:

    // If you want for a specific Enum
    private static string EnumStringValue(GroupTypes e)
    {
        return EnumStringValue<GroupTypes>(e);
    }

    // Generic
    private static string EnumStringValue<T>(T enumInstance)
    {
        return Enum.GetName(typeof(T), enumInstance);
    } 

次に、あなたが持っているものをラップするだけです

EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part

または

EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic

1

@EvenMienから取得し、コメントの一部に追加しました。(私自身の使用例でも)

public struct AgentAction
{
    private AgentAction(string value) { Value = value; }

    public string Value { get; private set; }

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

    public static AgentAction Login = new AgentAction("Logout");
    public static AgentAction Logout = new AgentAction("Logout");

    public static implicit operator string(AgentAction action) { return action.ToString(); }
}

1

このクラスを追加する

public class DatabasePreference {
    public DatabasePreference([CallerMemberName] string preferenceName = "") {
        PreferenceName = preferenceName;
    }
    public string PreferenceName;
}

この作業はCallerMemberName、コーディングを最小限に抑えるために使用されています

使用:

//Declare names
public static DatabasePreference ScannerDefaultFlashLight = new DatabasePreference();
public static DatabasePreference ScannerQrCodes = new DatabasePreference();
public static DatabasePreference Scanner1dCodes = new DatabasePreference();

試して:

Console.WriteLine(ScannerDefaultFlashLight.PreferenceName);
Console.WriteLine(ScannerDefaultFlashLight.Scanner1dCodes);

出力:

ScannerDefaultFlashLight
Scanner1dCodes

0

他の意見に基づいて、これは私が思いついたものです。このアプローチにより、定数値を取得する場所に.Valueを入力する必要がなくなります。

次のようなすべての文字列列挙の基本クラスがあります。

using System;
using Newtonsoft.Json;

[JsonConverter(typeof(ConstantConverter))]
public class StringEnum: IConvertible
{
    public string Value { get; set; }

    protected StringEnum(string value)
    {
        Value = value;
    }

    public static implicit operator string(StringEnum c)
    {
        return c.Value;
    }
    public string ToString(IFormatProvider provider)
    {
        return Value;
    }

    public TypeCode GetTypeCode()
    {
        throw new NotImplementedException();
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }
    //The same for all the rest of IConvertible methods
}

JsonConverterは次のようになります。

using System;
using Newtonsoft.Json;

class ConstantConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            serializer.Serialize(writer, null);
        }
        else
        {
            serializer.Serialize(writer, value.ToString());
        }
    }
}

そして実際の文字列列挙は次のようになります:

public sealed class Colors : StringEnum
{
    public static Colors Red { get { return new Catalog("Red"); } }
    public static Colors Yellow { get { return new Catalog("Yellow"); } }
    public static Colors White { get { return new Catalog("White"); } }

    private Colors(string value) : base(value) { }
}

これにより、Color.Redを使用して、Valueプロパティを使用せずにjsonにシリアル化することもできます


0

属性に文字列を格納するような堅牢なものは必要ありませんでした。私はちょうどのようなものを回すのに必要なMyEnum.BillEveryWeek「毎週法案」に、またはMyEnum.UseLegacySystem「使用のレガシーシステム」へ-基本的にindividiual小文字の単語にそのキャメル・ケースで列挙型を分割します。

public static string UnCamelCase(this Enum input, string delimiter = " ", bool preserveCasing = false)
{
    var characters = input.ToString().Select((x, i) =>
    {

       if (i > 0 && char.IsUpper(x))
       {
           return delimiter + x.ToString(CultureInfo.InvariantCulture);
       }
       return x.ToString(CultureInfo.InvariantCulture);

    });

    var result = preserveCasing
       ? string.Concat(characters)
       : string.Concat(characters).ToLower();

    var lastComma = result.LastIndexOf(", ", StringComparison.Ordinal);

    if (lastComma > -1)
    {
       result = result.Remove(lastComma, 2).Insert(lastComma, " and ");
    }

    return result;
}

MyEnum.UseLegacySystem.UnCamelCase() 「レガシーシステムを使用」を出力します

複数のフラグを設定している場合は、それをプレーンな英語に変換します(最後のカンマの代わりに「and」を除いてカンマ区切り)。

var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes;

Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.