예를 들어 ' 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가 제안한 두 가지 변환 방법을 사용할 때 매우 깔끔하게 작동합니다.
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;
그러면에서 throw 된를 Enum2
호출 Enum.IsDefined
하거나 잡을 필요없이에 존재하지 않는 입력 값을 처리 할 수 있습니다 . 매개 변수의 순서는 . ArgumentException
Enum.Parse
Enum.Parse
하나를 int로 캐스팅 한 다음 다른 열거 형으로 캐스팅합니다 (값에 따라 매핑을 수행하려는 경우 고려).
Gender2 gender2 = (Gender2)((int)gender1);
long
(또는 ulong
)에 의해 뒷받침되는 열거 형이있을 수 있습니다. ),이 경우 캐스트 가 오버플 로 될 수 있으며 정의해야하는 정의되지 않은 열거 형 값으로 끝납니다. int
int.MaxValue
int.MinValue
int
철저히하기 위해 일반적으로 Enum 1을 사용하고 Enum 2를 반환하는 함수와 Enum 2를 사용하여 Enum 1을 반환하는 함수 쌍을 만듭니다. 각 함수는 입력을 출력에 매핑하는 case 문으로 구성되며 기본 사례는 다음과 같은 예외를 발생시킵니다. 예상치 못한 값에 대해 불평하는 메시지.
이 특별한 경우에는 Male과 Female의 정수 값이 동일하다는 사실을 활용할 수 있지만, 나중에 열거 형이 변경되면 손상 될 수 있으므로 피하고 싶습니다.
다음이있는 경우 :
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
s에서 작동하는 세트 확장 메소드를 작성했습니다 . 특히 하나는 수행하려는 작업에 대해 작동 하고 다른 기본 유형을 가진 s 뿐만 아니라 Enum
s를 처리 합니다.FlagsAttribute
Enum
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
당신이하려는 것과 같은 유형을 변경합니다 .
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
s와 s 를 혼합하는 또 다른 부작용 FlagsAttribute
은 변환 프로세스가 값 사이에 일대일 일치를 초래하지 않는다는 것입니다.
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>();
}
이 경우 의 백업 값 이 8 이므로은 대신 letters
값을 갖습니다. 또한에서로 변환 하면 을 산출하여 직관적이지 않은 것처럼 보일 수 있습니다.Letters.H
Letters.D
Flavors.Peach
Flavors.Cherry | Flavors.Grape
Letters
Letters.C
위 의 Justin의 답변을 바탕으로 다음과 같이 생각했습니다.
/// <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;
}
}