열거 형을 다른 유형의 열거 형으로 변환


120

예를 들어 ' Gender'( Male =0 , Female =1) 열거 형이 있고 자체 Gender 열거 형 ( Male =0 , Female =1, Unknown =2) 이있는 서비스의 또 다른 열거 형이 있습니다.

내 질문은 열거 형에서 내 것으로 변환하기 위해 빠르고 멋진 것을 어떻게 작성할 수 있습니까?


6
"알 수 없음"을 무엇으로 변환 하시겠습니까?
Pavel Minaev

둘 다 동일한 값을 가질 때 열거 형을 다른 열거 형 유형으로 형변환 할 수 있습니다. ideone.com/7lgvgf
Gowtham S

답변:


87

확장 방법을 사용하면 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
    }
}

원하지 않는 경우 별도의 클래스를 사용할 필요가 없습니다. 내가 선호하는 것은 확장 메소드가 적용되는 클래스 / 구조 / 열거별로 그룹화되어있는 것입니다.


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; 그러면에서 throw 된를 Enum2호출 Enum.IsDefined하거나 잡을 필요없이에 존재하지 않는 입력 값을 처리 할 수 ​​있습니다 . 매개 변수의 순서는 . ArgumentExceptionEnum.ParseEnum.Parse
Sander

47

하나를 int로 캐스팅 한 다음 다른 열거 형으로 캐스팅합니다 (값에 따라 매핑을 수행하려는 경우 고려).

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

3
'야생에서'볼 가능성은 낮고 성별의 경우는 거의 같지 않지만 위 (또는 아래)에 정의 된 구성원 이있는 대신 long(또는 ulong)에 의해 뒷받침되는 열거 형이있을 수 있습니다. ),이 경우 캐스트 가 오버플 로 될 수 있으며 정의해야하는 정의되지 않은 열거 형 값으로 끝납니다. intint.MaxValueint.MinValueint
Rich O'Kelly 2013

물론이야. 올바른 방법은 (Gender2) ((여기에 기본 유형 삽입) gender1)이지만 위의 예가 올바른 아이디어를 제공한다고 생각하므로 변경하지 않을 것입니다.
아드리안 Zanescu

3
이렇게하려면 두 열거 형이 동일한 순서로 동일한 값을 가져야합니다. 이 특정 문제를 해결하는 동안 이것은 매우 부서지기 쉽고 일반적으로 열거 형 매핑에 사용하지 않습니다.
sonicblis 2013

2
음 .. 으악! . 매핑은 무언가를 기반으로해야합니다. 이 경우 매핑은 정수 값에 있습니다. 이름을 기반으로 매핑하려면 다른 코드가 필요합니다. 다른 종류의 매핑을 위해. 아무도이 "일반적으로 열거 매핑"이며 수단 "일반적으로 매핑"무엇을 지정하기 위해 시도 할 수 있습니다하지 않는 경우가 존재하지 않는다고 말했다
아드리안 Zanescu

20

철저히하기 위해 일반적으로 Enum 1을 사용하고 Enum 2를 반환하는 함수와 Enum 2를 사용하여 Enum 1을 반환하는 함수 쌍을 만듭니다. 각 함수는 입력을 출력에 매핑하는 case 문으로 구성되며 기본 사례는 다음과 같은 예외를 발생시킵니다. 예상치 못한 값에 대해 불평하는 메시지.

이 특별한 경우에는 Male과 Female의 정수 값이 동일하다는 사실을 활용할 수 있지만, 나중에 열거 형이 변경되면 손상 될 수 있으므로 피하고 싶습니다.


7
+1 열거 형의 정수 값을 사용하여 변환하려는 충동을 포기하는 개발자를 많이 보았지만 이는 오류가 발생하기 쉽습니다. 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

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)로 변환됩니다.
Justin

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

나는 몇 가지 다른 종류의 Enums에서 작동하는 세트 확장 메소드를 작성했습니다 . 특히 하나는 수행하려는 작업에 대해 작동 하고 다른 기본 유형을 가진 s 뿐만 아니라 Enums를 처리 합니다.FlagsAttributeEnum

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.ReadWriteWriteAccess Enum

Enums와 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.HLetters.DFlavors.PeachFlavors.Cherry | Flavors.GrapeLettersLetters.C


2

의 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
            );
        }
    }

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.