列挙型を別の型の列挙型に変換する


120

たとえば ' Gender'(Male =0 , Female =1)の列挙型があり、独自のGender列挙型を持つサービスから別の列挙型があります(Male =0 , Female =1, Unknown =2

私の質問は、列挙型から私のものに変換するための迅速で優れたものをどのように書くことができるかです


6
「unknown」を何に変換しますか?
Pavel Minaev 2009年

両方が同じ値である場合、列挙型を他の列挙型に型キャストできます。ideone.com/ 7lgvgf
Gowtham S

回答:


87

Nateによって提案された2つの変換メソッドを使用する場合、拡張メソッドの使用は非常にきちんと機能します。

public static class TheirGenderExtensions
{
    public static MyGender ToMyGender(this TheirGender value)
    {
        // insert switch statement here
    }
}

public static class MyGenderExtensions
{
    public static TheirGender ToTheirGender(this MyGender value)
    {
        // insert switch statement here
    }
}

必要がない場合は、個別のクラスを使用する必要はありません。私の好みは、拡張メソッドを適用するクラス/構造/列挙によってグループ化しておくことです。


233

与えられたEnum1 value = ...場合、名前で意味する場合:

Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());

数値を意味する場合、通常は次のようにキャストできます。

Enum2 value2 = (Enum2)value;

(ただし、キャストを使用するEnum.IsDefinedと、有効な値を確認するために使用できます)


16
これはより良い答えです
ニコラス

1
を使用するバージョンはEnum.Tryparse次のとおりです。 Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; これにより、によってスローされたs Enum2を呼び出しEnum.IsDefinedたりキャッチしたりすることなく、に存在しない入力値を処理できます。パラメータの順序が多かれ少なかれ逆になっていることに注意してください。ArgumentExceptionEnum.ParseEnum.Parse
サンダー

47

1つをintにキャストしてから、それを他の列挙型にキャストします(値に基づいてマッピングを実行することを考慮してください)。

Gender2 gender2 = (Gender2)((int)gender1);

3
それが「野生で」見られる可能性は低く、性別の場合もほとんどありませんが、上(または下)に定義されたメンバーを持つでなく、long(またはulong)で裏付けられた列挙が存在する可能性があります)。その場合、キャスト先がオーバーフローする可能性があり、定義する必要のある未定義の列挙値が生成されます。intint.MaxValueint.MinValueint
リッチオケリー2013

もちろん。正しい方法は(Gender2)((基になるタイプをここに挿入)gender1)ですが、上記の例は適切なアイデアを提供しているので、変更しません。
エイドリアンZanescu 2013

3
これには、2つの列挙型が同じ順序で同じ値を持つ必要があります。これはこの特定の問題を解決しますが、これは非常に脆弱であり、一般的に列挙型マッピングには使用しません。
sonicblis 2013年

2
えーと… 。マッピングは何かに基づいて行われる必要があります。この場合、マッピングは整数値に基づいています。名前に基づいてマッピングするには、別のコードが必要です。別の種類のマッピングの場合。これは「一般的な列挙型マッピング」であると誰も言っておらず、「マッピング一般」の意味を指定しようとしない限り、そのケースは存在しない
Adrian Zanescu

20

徹底的に私は通常、Enum 1を取りEnum 2を返す関数と、Enum 2を取りEnum 1を返す関数のペアを作成します。それぞれは、入力を出力にマッピングするCaseステートメントで構成され、デフォルトのCaseは、予期しない値について文句を言うメッセージ。

この特定のケースでは、男性と女性の整数値が同じであるという事実を利用できますが、ハックであり、いずれかの列挙型が将来変更されると破損する可能性があるため、これは避けます。


7
+1多くの開発者がenumの整数値を使用して変換する衝動をあきらめるのを見てきましたが、これは非常にエラーが発生しやすくなります。2つの関数を作成する古い学校の方法は、時間の経過とともにその価値を証明しています...
Hemant

20

私たちが持っている場合:

enum Gender
{
    M = 0,
    F = 1,
    U = 2
}

そして

enum Gender2
{
    Male = 0,
    Female = 1,
    Unknown = 2
}

安全にできる

var gender = Gender.M;
var gender2   = (Gender2)(int)gender;

あるいは

var enumOfGender2Type = (Gender2)0;

'='記号の右側の列挙型が左側の列挙型よりも多くの値を持っている場合をカバーしたい場合-他の人が示唆するように、それをカバーする独自のメソッド/辞書を記述する必要があります。


あなたの答えは質問をするようなものです!?はいの場合、これは答えではなく、いいえの場合、上記の同様の答えがあります;)。
shA.t 2016

13

あなたはこのような単純な一般的な拡張メソッドを書くことができます

public static T ConvertTo<T>(this object value)            
    where T : struct,IConvertible
{
    var sourceType = value.GetType();
    if (!sourceType.IsEnum)
        throw new ArgumentException("Source type is not enum");
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Destination type is not enum");
    return (T)Enum.Parse(typeof(T), value.ToString());
}

1
上記の回答で提案されているように、欠損値のケースはカバーしていません。その場合もカバーするこの拡張メソッドを変更する必要があります。
eRaisedToX

8

次のような簡単な関数を書くことができます:

public static MyGender ConvertTo(TheirGender theirGender)
{
    switch(theirGender)
    {
        case TheirGender.Male:
            break;//return male
        case TheirGender.Female:
            break;//return female
        case TheirGender.Unknown:
            break;//return whatever
    }
}

1
これは関数ではありません。「MyGender」が期待され、「void」が返されます
bl4ckr0se

7

誰かが興味を持っている場合の拡張メソッドのバージョンはこちらです

public static TEnum ConvertEnum<TEnum >(this Enum source)
    {
        return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
    }

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();

それは両方の列挙が同じ数値を持っていることを意味しませんか?
kuskmen

1
いいえ、これは名前で文字列に変換しています。そのため、Enum.Foo(1)は、数値が異なっていてもEnum2.Foo(2)に変換されます。
ジャスティン

3
public static TEnum ConvertByName<TEnum>(this Enum source, bool ignoreCase = false) where TEnum : struct
{
    // if limited by lack of generic enum constraint
    if (!typeof(TEnum).IsEnum)
    {
        throw new InvalidOperationException("enumeration type required.");
    }

    TEnum result;
    if (!Enum.TryParse(source.ToString(), ignoreCase, out result))
    {
        throw new Exception("conversion failure.");
    }

    return result;
}

2

しばらく前に、いくつかの異なる種類ので機能するセット拡張メソッドを作成しましたEnum。特に、達成しようとしているものに対して機能Enumし、でsを処理するFlagsAttributeだけでなくEnum、異なる基本型を持つsを処理します。

public static tEnum SetFlags<tEnum>(this Enum e, tEnum flags, bool set, bool typeCheck = true) where tEnum : IComparable
{
    if (typeCheck)
    {
        if (e.GetType() != flags.GetType())
            throw new ArgumentException("Argument is not the same type as this instance.", "flags");
    }

    var flagsUnderlyingType = Enum.GetUnderlyingType(typeof(tEnum));

    var firstNum = Convert.ToUInt32(e);
    var secondNum = Convert.ToUInt32(flags);

    if (set)
        firstNum |= secondNum;

    else
        firstNum &= ~secondNum;

    var newValue = (tEnum)Convert.ChangeType(firstNum, flagsUnderlyingType);

    if (!typeCheck)
    {
        var values = Enum.GetValues(typeof(tEnum));
        var lastValue = (tEnum)values.GetValue(values.Length - 1);

        if (newValue.CompareTo(lastValue) > 0)
            return lastValue;
    }

    return newValue;
}

そこから、他のより具体的な拡張メソッドを追加できます。

public static tEnum AddFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, true);
}

public static tEnum RemoveFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, false);
}

これはEnum、あなたがやろうとしているようなsのタイプを変更します。

public static tEnum ChangeType<tEnum>(this Enum e) where tEnum : IComparable
{
    return SetFlags(e, default(tEnum), true, false);
}

ただし、この方法を使用すると、フラグのないものでも、Enum他のものと変換できることに注意してくださいEnum。例えば:

public enum Turtle
{
    None = 0,
    Pink,
    Green,
    Blue,
    Black,
    Yellow
}

[Flags]
public enum WriteAccess : short
{
   None = 0,
   Read = 1,
   Write = 2,
   ReadWrite = 3
}

static void Main(string[] args)
{
    WriteAccess access = WriteAccess.ReadWrite;
    Turtle turtle = access.ChangeType<Turtle>();
}

変数turtleの値はになりTurtle.Blueます。

ただし、Enumこの方法を使用すると、未定義の値から安全になります。例えば:

static void Main(string[] args)
{
    Turtle turtle = Turtle.Yellow;
    WriteAccess access = turtle.ChangeType<WriteAccess>();
}

この場合、の最大値は3であるため、はにaccess設定されます。WriteAccess.ReadWriteWriteAccess Enum

とを混在させた場合Enumのもう1つの副作用はFlagsAttribute、変換プロセスで値が1対1に一致しないことです。

public enum Letters
{
    None = 0,
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H
}

[Flags]
public enum Flavors
{
    None = 0,
    Cherry = 1,
    Grape = 2,
    Orange = 4,
    Peach = 8
}

static void Main(string[] args)
{
    Flavors flavors = Flavors.Peach;
    Letters letters = flavors.ChangeType<Letters>();
}

この場合には、lettersの値がありますLetters.Hの代わりにLetters.Dのバッキング値がので、Flavors.Peachまた8であるから変換Flavors.Cherry | Flavors.GrapeLetters生じるであろうLetters.C、直感的に見えることができます。


2

上記のジャスティンの答えに基づいて私はこれを思いつきました:

    /// <summary>
    /// Converts Enum Value to different Enum Value (by Value Name) See https://stackoverflow.com/a/31993512/6500501.
    /// </summary>
    /// <typeparam name="TEnum">The type of the enum to convert to.</typeparam>
    /// <param name="source">The source enum to convert from.</param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public static TEnum ConvertTo<TEnum>(this Enum source)
    {
        try
        {
            return (TEnum) Enum.Parse(typeof(TEnum), source.ToString(), ignoreCase: true);
        }
        catch (ArgumentException aex)
        {
            throw new InvalidOperationException
            (
                $"Could not convert {source.GetType().ToString()} [{source.ToString()}] to {typeof(TEnum).ToString()}", aex
            );
        }
    }

1

私はそれが古い質問であり、多くの回答があることを知っていますが、受け入れられた回答のようにswitchステートメントを使用するのはやや面倒なので、ここに2セントを示します。

私の個人的なお気に入りの方法は、キーをソース列挙型、値をターゲット列挙型にするディクショナリを使用することです。そのため、質問で提示された場合、コードは次のようになります。

var genderTranslator = new Dictionary<TheirGender, MyGender>();
genderTranslator.Add(TheirGender.Male, MyGender.Male);
genderTranslator.Add(TheirGender.Female, MyGender.Female);
genderTranslator.Add(TheirGender.Unknown, MyGender.Unknown);

// translate their to mine    
var myValue = genderTranslator[TheirValue];

// translate mine to their
var TheirValue = genderTranslator .FirstOrDefault(x => x.Value == myValue).Key;;

もちろん、これは静的クラスにラップして、拡張メソッドとして使用できます。

public static class EnumTranslator
{

    private static Dictionary<TheirGender, MyGender> GenderTranslator = InitializeGenderTranslator();

    private static Dictionary<TheirGender, MyGender> InitializeGenderTranslator()
    {
        var translator = new Dictionary<TheirGender, MyGender>();
        translator.Add(TheirGender.Male, MyGender.Male);
        translator.Add(TheirGender.Female, MyGender.Female);
        translator.Add(TheirGender.Unknown, MyGender.Unknown);
        return translator;
    }

    public static MyGender Translate(this TheirGender theirValue)
    {
        return GenderTranslator[theirValue];
    }

    public static TheirGender Translate(this MyGender myValue)
    {
        return GenderTranslator.FirstOrDefault(x => x.Value == myValue).Key;
    }

}

辞書を作成するために両方の列挙を列挙することもできるので、このアプローチが好きです。(もちろん、同じ順序である場合)
AlexS

0

ToString()を使用して最初の列挙型をその名前に変換し、次にEnum.Parse()を使用して文字列を別の列挙型に戻すことができます。値が宛先の列挙型でサポートされていない場合(つまり、「不明」の値の場合)、これは例外をスローします。

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