文字列値で列挙型を定義する方法は?


97

を定義して、EnumCSVまたは類似のファイルで使用される有効な共通の区切り記号を追加しようとしています。次に、それをComboBoxデータソースとしてにバインドするので、Enum定義に追加または削除するたびに、コンボボックスの内容を変更する必要はありません。

問題は、次のような文字列表現で列挙型をどのように定義できるかです。

public enum SeparatorChars{Comma = ",", Tab = "\t", Space = " "}


回答:


112

できません-列挙値は整数値でなければなりません。属性を使用して文字列値を各列挙値に関連付けることができます。この場合、すべてのセパレーターが単一の文字である場合は、char値を使用できます。

enum Separator
{
    Comma = ',',
    Tab = '\t',
    Space = ' '
}

(編集:明確にするためcharに、列挙型の基本型を作成することはできませんが、char定数を使用して各列挙値に対応する整数値を割り当てることができます。上記の列挙型の基本型はintです。)

次に、必要な場合は拡張メソッド:

public string ToSeparatorString(this Separator separator)
{
    // TODO: validation
    return ((char) separator).ToString();
}

Charは列挙型では無効です。Msdn:「すべての列挙型には基礎となる型があり、char以外の任意の整数型にすることができます。」
dowhile for

8
@dowhilefor:私の回答のとおり、値には文字リテラルを使用できます。私はそれをテストしました:)
Jon Skeet

この要件はファイルに対するものであるため、ユーザーはCRLF区切り文字を必要とする場合があります。その場合にも機能しますか?
Maheep、2011

ジョン、\ tはcharとしてカウントされますか?!
Saeid Yazdani、2011

1
@ShaunLuttin:列挙型は単に「名前付きの番号」です-したがって、文字列列挙型は実際にはそのモデルにまったく適合しません。
Jon Skeet 2017

82

私の知る限り、列挙型に文字列値を割り当てることはできません。あなたができることは、その中に文字列定数を持つクラスを作成することです。

public static class SeparatorChars
{
    public static String Comma { get { return ",";} } 
    public static String Tab { get { return "\t,";} } 
    public static String Space { get { return " ";} } 
}

9
他の人に対抗するこのアプローチの欠点は、何か特別な/特別なことをしないとこれらを列挙できないことです。
caesay

これは、有効な値が制限されseparatorSeparator型ではなく文字列(何でもかまいません)になるため、コンパイル時に特定の値を強制するのに役立ちません。
ChickenFeet

70

あなたはそれを達成することができますが、少しの作業が必要になります。

  1. enumの文字列値を含む属性クラスを定義します。
  2. 属性から値を返す拡張メソッドを定義します。たとえば、GetStringValue(このEnum値)は属性値を返します。
  3. 次に、このような列挙型を定義できます。
public enum Test:int {
    [StringValue( "a")]
    Foo = 1、
    [StringValue( "b")]
    何か= 2        
} 
  1. Attrinbute Test.Foo.GetStringValue();から値を取得するには、

参照:C#の文字列値を持つ列挙型


4
私はこれが古いことを知っていますが、明らかにユニークであり、コードで列挙型を使用し、DBで文字列値を使用できます。素晴らしい
A_kat

1
別の遅いコメントですが、これは本当に素晴らしい解決策です
Alan

34

文字列値(または他のタイプ)の単純な列挙の場合:

public static class MyEnumClass
{
    public const string 
        MyValue1 = "My value 1",
        MyValue2 = "My value 2";
}

使用法: string MyValue = MyEnumClass.MyValue1;


1
これは列挙型ではありませんが、ユーザーがやろうとしていることに対して最善の解決策になると思います。時には、最も簡単な解決策が最善です。
Zesty

28

列挙型ではこれを行うことはできませんが、次のように行うことができます。

public static class SeparatorChars
{
    public static string Comma = ",";

    public static string Tab = "\t";

    public static string Space = " ";
}

1
+1正しい解決策だと思いますが、クラスの名前を変更するか、タイプを文字に変更します。一貫しているだけです。
dowhile for

ありがとう、comboBox.DataSource = Enum.GetValues(typeof(myEnum));この場合何に相当するかわかりますか?
Saeid Yazdani、2011

1
@ Sean87:私はあなたがそれを持ちたいと思っています、私はJonSkeetsの答えを取ります。
Fischermaen

これはswitch-caseブロック内では使用できないため、ほぼ正しい答えだと思います。フィールドはする必要がありますconst。しかし、あなたがしたいのなら、それでもなお仕方がありませんEnum.GetValues(typeof(myEnum))
アンドレ・Santaló

7
const代わりに使用しますstatic。定数は静的であるのと同様に読み取り専用であり、(読み取り専用フィールドを除いて)コンストラクターで割り当て可能ではありません。
Olivier Jacot-Descombes 2015

12

enumはプリミティブ数値型にのみ基づいているため、できません。Dictionary代わりに使用してみることができます:

Dictionary<String, char> separators = new Dictionary<string, char>
{
    {"Comma", ','}, 
    {"Tab",  '\t'}, 
    {"Space", ' '},
};

または、a Dictionary<Separator, char>またはDictionary<Separator, string>where Separatorを通常の列挙型として使用することもできます。

enum Separator
{
    Comma,
    Tab,
    Space
}

これは、文字列を直接処理するよりも少し快適です。


11

列挙型の動作をエミュレートするが、string代わりにを使用するクラスはint、次のように作成できます...

public class GrainType
{
    private string _typeKeyWord;

    private GrainType(string typeKeyWord)
    {
        _typeKeyWord = typeKeyWord;
    }

    public override string ToString()
    {
        return _typeKeyWord;
    }

    public static GrainType Wheat = new GrainType("GT_WHEAT");
    public static GrainType Corn = new GrainType("GT_CORN");
    public static GrainType Rice = new GrainType("GT_RICE");
    public static GrainType Barley = new GrainType("GT_BARLEY");

}

使用法...

GrainType myGrain = GrainType.Wheat;

PrintGrainKeyword(myGrain);

その後...

public void PrintGrainKeyword(GrainType grain) 
{
    Console.Writeline("My Grain code is " + grain.ToString());   // Displays "My Grain code is GT_WHEAT"
}

唯一のことはGrainType myGrain = "GT_CORN"、例えばあなたができないことです。
colmde 2018

オペレーターをオーバーライドした場合
SSX-SL33PY 2018

8

答えはちょっと遅いですが、将来誰かを助けるかもしれません。この種の問題にはstructを使用する方が簡単だと思いました。

次のサンプルは、MSコードから貼り付けた部分をコピーしたものです。

namespace System.IdentityModel.Tokens.Jwt
{
    //
    // Summary:
    //     List of registered claims from different sources http://tools.ietf.org/html/rfc7519#section-4
    //     http://openid.net/specs/openid-connect-core-1_0.html#IDToken
    public struct JwtRegisteredClaimNames
    {
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Actort = "actort";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Typ = "typ";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Sub = "sub";
        //
        // Summary:
        //     http://openid.net/specs/openid-connect-frontchannel-1_0.html#OPLogout
        public const string Sid = "sid";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Prn = "prn";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nbf = "nbf";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nonce = "nonce";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string NameId = "nameid";

    }
}

このアプローチがクラスを使用するよりも優れている理由を説明していただけますか?
Gerardo Grignoli

@GerardoGrignoliなぜこのようなことのためにMSでクラスの代わりに構造体を使用するのか正確にはわかりません。これは完全に私のために働いているので、私はそれを見つけようともしませんでした。たぶんここでスタックで質問してみてください...
suchoss

5

より一般的な質問への回答を探してここに到着した人にとって、コードをのようにしたい場合は、静的クラスの概念を拡張できますenum

次のアプローチは、必要なを確定していない場合に機能しenum namesenum valuesはのstring表現ですenam namenameof()リファクタリングを簡単にするために使用します。

public static class Colours
{
    public static string Red => nameof(Red);
    public static string Green => nameof(Green);
    public static string Blue => nameof(Blue);
}

これにより、文字列値(次の疑似コードなど)を持つ列挙型の意図が実現されます。

public enum Colours
{
    "Red",
    "Green",
    "Blue"
}

5

遅すぎるかもしれませんが、ここに行きます。

EnumMember属性を使用してEnum値を管理できます。

public enum EUnitOfMeasure
{
    [EnumMember(Value = "KM")]
    Kilometer,
    [EnumMember(Value = "MI")]
    Miles
}

このようにして、EUnitOfMeasureの結果値はKMまたはMIになります。これは、Andrew Whitakerの回答でも見られます


4

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

使用法:

///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = New("#FF0000");
    public static readonly HexColor Green = New("#00FF00");
    public static readonly HexColor Red = New("#000FF");
}

特徴

  • StringEnumは、通常の列挙型にやや似ています。
    // 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)
  • Intellisenseは、クラスにxmlコメントで注釈が付けられている場合、列挙名を提案し<completitionlist>ます。(C#とVBの両方で機能):ie

Intellisenseデモ

インストール

どちらか:

  • > = 1.0、> = 4.5、> = 4.6などで動作するように基づいている最新のStringEnum NuGetパッケージをインストールします。.Net Standard 1.0.Net Core.Net FrameworkMono
  • または、次のStringEnum基本クラスをプロジェクトに貼り付けます。(最新バージョン
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static IList<T> valueList = new List<T>();
        protected static T New(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueList.Add(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 sensitivity.</param>
        public static T Parse(string value, bool caseSensitive = false)
        {
            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 sensitivity.</param>
        public static T TryParse(string value, bool caseSensitive = false)
        {
            if (value == null) return null;
            if (valueList.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            var field = valueList.FirstOrDefault(f => f.Value.Equals(value,
                    caseSensitive ? StringComparison.Ordinal
                                  : StringComparison.OrdinalIgnoreCase));
            // Not using InvariantCulture because it's only supported in NETStandard >= 2.0

            if (field == null)
                return null;

            return field;
        }
    }
  • 以下のためにNewtonsoft.Jsonシリアル化のサポート、代わりにこの拡張バージョンをコピーします。StringEnum.cs

このコードがベンの答えに似ているという事実の後で、私は気付きました。私は心からそれを一から書いた。しかし、<completitionlist>ハックのようにいくつかの追加機能があると思います。結果のクラスはEnumのように見え、Parse()、NuGetパッケージ、およびリポジトリにリフレクションを使用しないので、着信の問題とフィードバックに対処できると期待しています。


3

ここでいくつかの回答に基づいて、列挙string型の動作を模倣する再利用可能な基本クラスを実装しました。次のようなさまざまな操作をサポートしています。

  1. 可能な値のリストを取得する
  2. 文字列に変換する
  3. 他のインスタンスとの比較を通じて.Equals==、および!=
  4. JSON.NET JsonConverterを使用したJSONとの変換

これは全体としての基本クラスです。

public abstract class StringEnumBase<T> : IEquatable<T>
    where T : StringEnumBase<T>
{
    public string Value { get; }

    protected StringEnumBase(string value) => this.Value = value;

    public override string ToString() => this.Value;

    public static List<T> AsList()
    {
        return typeof(T)
            .GetProperties(BindingFlags.Public | BindingFlags.Static)
            .Where(p => p.PropertyType == typeof(T))
            .Select(p => (T)p.GetValue(null))
            .ToList();
    }

    public static T Parse(string value)
    {
        List<T> all = AsList();

        if (!all.Any(a => a.Value == value))
            throw new InvalidOperationException($"\"{value}\" is not a valid value for the type {typeof(T).Name}");

        return all.Single(a => a.Value == value);
    }

    public bool Equals(T other)
    {
        if (other == null) return false;
        return this.Value == other?.Value;
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (obj is T other) return this.Equals(other);
        return false;
    }

    public override int GetHashCode() => this.Value.GetHashCode();

    public static bool operator ==(StringEnumBase<T> a, StringEnumBase<T> b) => a?.Equals(b) ?? false;

    public static bool operator !=(StringEnumBase<T> a, StringEnumBase<T> b) => !(a?.Equals(b) ?? false);

    public class JsonConverter<T> : Newtonsoft.Json.JsonConverter
        where T : StringEnumBase<T>
    {
        public override bool CanRead => true;

        public override bool CanWrite => true;

        public override bool CanConvert(Type objectType) => ImplementsGeneric(objectType, typeof(StringEnumBase<>));

        private static bool ImplementsGeneric(Type type, Type generic)
        {
            while (type != null)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
                    return true;

                type = type.BaseType;
            }

            return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken item = JToken.Load(reader);
            string value = item.Value<string>();
            return StringEnumBase<T>.Parse(value);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value is StringEnumBase<T> v)
                JToken.FromObject(v.Value).WriteTo(writer);
        }
    }
}

そして、これが「文字列列挙」を実装する方法です:

[JsonConverter(typeof(JsonConverter<Colour>))]
public class Colour : StringEnumBase<Colour>
{
    private Colour(string value) : base(value) { }

    public static Colour Red => new Colour("red");
    public static Colour Green => new Colour("green");
    public static Colour Blue => new Colour("blue");
}

これは次のように使用できます:

public class Foo
{
    public Colour colour { get; }

    public Foo(Colour colour) => this.colour = colour;

    public bool Bar()
    {
        if (this.colour == Colour.Red || this.colour == Colour.Blue)
            return true;
        else
            return false;
    }
}

誰かがこれが便利だと思うことを願っています!


2

まず、文字ではなくても、文字ではなく文字列を割り当てようとします。「、」の代わりに「、」を使用します。次に、列挙型charはユニコード値を使用せずに整数型のみを取りますが、使用しないことを強くお勧めします。異なる文化や言語でこれらの値が同じままであることが確かな場合は、const文字列を持つ静的クラスを使用します。


2

a charまたはa stringを列挙型のベースとして使用することは実際には不可能ですが、これはあなたが本当にやりたいことではないと思います。

あなたが述べたように、可能性の列挙を持ち、コンボボックス内にこれの文字列表現を表示したいと思います。ユーザーがこれらの文字列表現のいずれかを選択した場合、対応する列挙型を取得します。そしてこれは可能です:

最初に、文字列を列挙値にリンクする必要があります。これDescriptionAttributeは、ここまたはここで説明されているようなものを使用して行うことができます

次に、列挙値と対応する説明のリストを作成する必要があります。これは、次の方法を使用して実行できます。

/// <summary>
/// Creates an List with all keys and values of a given Enum class
/// </summary>
/// <typeparam name="T">Must be derived from class Enum!</typeparam>
/// <returns>A list of KeyValuePair&lt;Enum, string&gt; with all available
/// names and values of the given Enum.</returns>
public static IList<KeyValuePair<T, string>> ToList<T>() where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be an enum");
    }

    return (IList<KeyValuePair<T, string>>)
            Enum.GetValues(type)
                .OfType<T>()
                .Select(e =>
                {
                    var asEnum = (Enum)Convert.ChangeType(e, typeof(Enum));
                    return new KeyValuePair<T, string>(e, asEnum.Description());
                })
                .ToArray();
}

これで、すべての列挙型とその説明のキーと値のペアのリストが表示されます。したがって、これをコンボボックスのデータソースとして単に割り当てましょう。

var comboBox = new ComboBox();
comboBox.ValueMember = "Key"
comboBox.DisplayMember = "Value";
comboBox.DataSource = EnumUtilities.ToList<Separator>();

comboBox.SelectedIndexChanged += (sender, e) =>
{
    var selectedEnum = (Separator)comboBox.SelectedValue;
    MessageBox.Show(selectedEnum.ToString());
}

ユーザーは列挙型のすべての文字列表現を確認し、コード内で目的の列挙値を取得します。


0

列挙型を文字列型として定義することはできません。enumの承認されたタイプは、byte、sbyte、short、ushort、int、uint、long、またはulongです。

列挙の詳細が必要な場合は、以下のリンクに従ってください。そのリンクは、列挙を理解するのに役立ちます。 列挙

ゆうたろう


0

わたしにはできる..

   public class ShapeTypes
    {
        private ShapeTypes() { }
        public static string OVAL
        {
            get
            {
                return "ov";
            }
            private set { }
        }

        public static string SQUARE
        {
            get
            {
                return "sq";
            }
            private set { }
        }

        public static string RECTANGLE
        {
            get
            {
                return "rec";
            }
            private set { }
        }
    }


-1

列挙クラス

 public sealed class GenericDateTimeFormatType
    {

        public static readonly GenericDateTimeFormatType Format1 = new GenericDateTimeFormatType("dd-MM-YYYY");
        public static readonly GenericDateTimeFormatType Format2 = new GenericDateTimeFormatType("dd-MMM-YYYY");

        private GenericDateTimeFormatType(string Format)
        {
            _Value = Format;
        }

        public string _Value { get; private set; }
    }

エニュマレーション消耗

public static void Main()
{
       Country A = new Country();

       A.DefaultDateFormat = GenericDateTimeFormatType.Format1;

      Console.ReadLine();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.