program tip

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

radiobox 2020. 8. 8. 12:19
반응형

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


예를 들어 ' 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하면 유효한 값을 확인하는 데 사용할 수 있습니다. )


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

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

철저히하기 위해 일반적으로 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;
}

나는 몇 가지 다른 종류의 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);
}

Be warned, though, that you CAN convert between any Enum and any other Enum using this method, even those that do not have flags. For example:

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

The variable turtle will have a value of Turtle.Blue.

However, there is safety from undefined Enum values using this method. For instance:

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

In this case, access will be set to WriteAccess.ReadWrite, since the WriteAccess Enum has a maximum value of 3.

Another side effect of mixing Enums with the FlagsAttribute and those without it is that the conversion process will not result in a 1 to 1 match between their values.

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

In this case, letters will have a value of Letters.H instead of Letters.D, since the backing value of Flavors.Peach is 8. Also, a conversion from Flavors.Cherry | Flavors.Grape to Letters would yield Letters.C, which can seem unintuitive.


Based on Justin's answer above I came up with this:

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

I know that's an old question and have a lot of answers, However I find that using a switch statement as in the accepted answer is somewhat cumbersome, so here are my 2 cents:

My personal favorite method is to use a dictionary, where the key is the source enum and the value is the target enum - so in the case presented on the question my code would look like this:

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;;

Of course, this can be wrapped in a static class and be used as an extension methods:

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

}

You can use ToString() to convert the first enum to its name, and then Enum.Parse() to convert the string back to the other Enum. This will throw an exception if the value is not supported by the destination enum (i.e. for an "Unknown" value)

참고URL : https://stackoverflow.com/questions/1818131/convert-an-enum-to-another-type-of-enum

반응형