たとえば ' Gender
'(Male =0 , Female =1
)の列挙型があり、独自のGender列挙型を持つサービスから別の列挙型があります(Male =0 , Female =1, Unknown =2
)
私の質問は、列挙型から私のものに変換するための迅速で優れたものをどのように書くことができるかです
たとえば ' Gender
'(Male =0 , Female =1
)の列挙型があり、独自のGender列挙型を持つサービスから別の列挙型があります(Male =0 , Female =1, Unknown =2
)
私の質問は、列挙型から私のものに変換するための迅速で優れたものをどのように書くことができるかです
回答:
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
}
}
必要がない場合は、個別のクラスを使用する必要はありません。私の好みは、拡張メソッドを適用するクラス/構造/列挙によってグループ化しておくことです。
与えられたEnum1 value = ...
場合、名前で意味する場合:
Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());
数値を意味する場合、通常は次のようにキャストできます。
Enum2 value2 = (Enum2)value;
(ただし、キャストを使用するEnum.IsDefined
と、有効な値を確認するために使用できます)
Enum.Tryparse
次のとおりです。 Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown;
これにより、によってスローされたs Enum2
を呼び出しEnum.IsDefined
たりキャッチしたりすることなく、に存在しない入力値を処理できます。パラメータの順序が多かれ少なかれ逆になっていることに注意してください。ArgumentException
Enum.Parse
Enum.Parse
1つをintにキャストしてから、それを他の列挙型にキャストします(値に基づいてマッピングを実行することを考慮してください)。
Gender2 gender2 = (Gender2)((int)gender1);
long
(またはulong
)で裏付けられた列挙が存在する可能性があります)。その場合、キャスト先がオーバーフローする可能性があり、定義する必要のある未定義の列挙値が生成されます。int
int.MaxValue
int.MinValue
int
徹底的に私は通常、Enum 1を取りEnum 2を返す関数と、Enum 2を取りEnum 1を返す関数のペアを作成します。それぞれは、入力を出力にマッピングするCaseステートメントで構成され、デフォルトのCaseは、予期しない値について文句を言うメッセージ。
この特定のケースでは、男性と女性の整数値が同じであるという事実を利用できますが、ハックであり、いずれかの列挙型が将来変更されると破損する可能性があるため、これは避けます。
私たちが持っている場合:
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;
'='記号の右側の列挙型が左側の列挙型よりも多くの値を持っている場合をカバーしたい場合-他の人が示唆するように、それをカバーする独自のメソッド/辞書を記述する必要があります。
あなたはこのような単純な一般的な拡張メソッドを書くことができます
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());
}
次のような簡単な関数を書くことができます:
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
}
}
誰かが興味を持っている場合の拡張メソッドのバージョンはこちらです
public static TEnum ConvertEnum<TEnum >(this Enum source)
{
return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
}
// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();
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;
}
しばらく前に、いくつかの異なる種類ので機能するセット拡張メソッドを作成しました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.ReadWrite
WriteAccess
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.Grape
にLetters
生じるであろうLetters.C
、直感的に見えることができます。
上記のジャスティンの答えに基づいて私はこれを思いつきました:
/// <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
);
}
}
私はそれが古い質問であり、多くの回答があることを知っていますが、受け入れられた回答のように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;
}
}
ToString()を使用して最初の列挙型をその名前に変換し、次にEnum.Parse()を使用して文字列を別の列挙型に戻すことができます。値が宛先の列挙型でサポートされていない場合(つまり、「不明」の値の場合)、これは例外をスローします。