converter um enum em outro tipo de enum

120

Eu tenho um enum de, por exemplo, ' Gender' ( Male =0 , Female =1) e tenho outro enum de um serviço que tem seu próprio enum de gênero ( Male =0 , Female =1, Unknown =2)

Minha pergunta é como posso escrever algo rápido e agradável para converter de seu enum para o meu?

kurasa
fonte
6
Para o que você deseja converter "desconhecido"?
Pavel Minaev
Você pode converter o enum para outros tipos de enum quando ambos tiverem os mesmos valores, consulte ideone.com/7lgvgf
Gowtham S

Respostas:

87

Usar um método de extensão funciona perfeitamente, ao usar os dois métodos de conversão sugeridos por 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
    }
}

Obviamente, não há necessidade de usar classes separadas se você não quiser. Minha preferência é manter os métodos de extensão agrupados pelas classes / estruturas / enumerações às quais se aplicam.

Zooba
fonte
233

Dado Enum1 value = ..., então, se você quer dizer por nome:

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

Se você quer dizer com valor numérico, geralmente pode apenas lançar:

Enum2 value2 = (Enum2)value;

(com o elenco, você pode querer usar Enum.IsDefinedpara verificar os valores válidos, no entanto)

Marc Gravell
fonte
16
Esta é a melhor resposta
Nicholas
1
Aqui está uma versão que usa Enum.Tryparse: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; Isso permitirá que você manipule os valores de entrada que não existem Enum2sem a necessidade de chamar Enum.IsDefinedou capturar os ArgumentExceptions lançados por Enum.Parse. Observe que a ordem dos parâmetros é mais ou menos invertida Enum.Parse.
Sander
47

Basta converter um para int e, em seguida, convertê-lo para o outro enum (considerando que você deseja que o mapeamento seja feito com base no valor):

Gender2 gender2 = (Gender2)((int)gender1);
Adrian Zanescu
fonte
3
Embora seja improvável vê-lo 'na natureza', e é altamente improvável que seja o caso para os gêneros, pode haver algum enum que é apoiado por um long(ou ulong) em vez de um intque tem membros definidos que estão acima int.MaxValue(ou abaixo int.MinValue), caso em que a conversão para intpoderia estourar e você acabaria com um valor enum indefinido que deve ser definido.
Rich O'Kelly
claro. a maneira correta seria (Gênero2) ((insira o tipo subjacente aqui) gênero1), mas acho que o exemplo acima dá a ideia certa, então não vou alterá-la.
Adrian Zanescu
3
Isso requer que os dois enums tenham os mesmos valores na mesma ordem. Embora resolva esse problema específico, isso é realmente frágil e eu não usaria isso para mapeamento enum em geral.
sonicblis
2
bem .... duh! . O mapeamento precisa ser feito com base em algo. Neste caso, o mapeamento é em valor integral. Para mapear com base no nome, você precisa de um código diferente. Para outro tipo de mapeamento, algo mais. Ninguém disse que isso é "para mapeamento enum em geral" e esse caso não existe a menos que você possa tentar especificar o que significa "mapeamento em geral"
Adrian Zanescu
20

Para ser mais completo, normalmente crio um par de funções, uma que leva Enum 1 e retorna Enum 2 e outra que leva Enum 2 e retorna Enum 1. Cada uma consiste em uma instrução de caso que mapeia entradas para saídas e o caso padrão lança uma exceção com um mensagem reclamando de um valor inesperado.

Neste caso específico, você pode tirar vantagem do fato de que os valores inteiros de Masculino e Feminino são os mesmos, mas eu evitaria isso, pois é hackeado e sujeito a quebras se qualquer enum mudar no futuro.

Nate CK
fonte
7
+1 Tenho visto muitos desenvolvedores desistindo de usar o valor inteiro de enums para convertê-los, mas isso é muito sujeito a erros. O método da velha escola de escrever 2 funções provou seu valor ao longo do tempo ...
Hemant
20

Se tiver-mos:

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

e

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

Podemos fazer com segurança

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

Ou mesmo

var enumOfGender2Type = (Gender2)0;

Se você quiser cobrir o caso em que um enum no lado direito do sinal '=' tem mais valores do que o enum no lado esquerdo - você terá que escrever seu próprio método / dicionário para cobrir isso como outros sugeriram.

Nedcode
fonte
Sua resposta é como fazer uma pergunta !? Se sim esta não é uma resposta e se não há uma resposta semelhante acima ;).
shA.t de
13

Você poderia escrever um método de extensão genérico simples como este

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());
}
Jishnu AP
fonte
1
Não cobre o caso de valores ausentes, conforme sugerido nas respostas acima. Você deve modificar este método de extensão cobrindo esse caso também.
eRaisedToX
8

você pode escrever uma função simples como a seguinte:

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
    }
}
RCIX
fonte
1
isso não é uma função. esperava 'MyGender' e você retornou 'void'
bl4ckr0se
7

Aqui está uma versão do método de extensão se alguém estiver interessado

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

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();
Justin
fonte
Isso não implica que ambas as enumerações tenham os mesmos valores numéricos?
Kuskmen de
1
Não, isso está convertendo por nome por string. Portanto, Enum.Foo (1) será convertido em Enum2.Foo (2), embora seus valores numéricos sejam diferentes.
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;
}
epistemofílico
fonte
2

Eu escrevi métodos de extensão de conjunto um tempo atrás que funcionam para vários tipos diferentes de programas Enum. Um em particular funciona para o que você está tentando realizar e lida Enumcom s com os FlagsAttribute, bem como Enumcom diferentes tipos subjacentes.

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

A partir daí, você pode adicionar outros métodos de extensão mais específicos.

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

Este mudará os tipos de Enums como você está tentando fazer.

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

Esteja avisado, porém, que você PODE converter entre qualquer Enume qualquer outro Enumusando este método, mesmo aqueles que não têm sinalizadores. Por exemplo:

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

A variável turtleterá um valor de Turtle.Blue.

No entanto, há segurança de Enumvalores indefinidos usando este método. Por exemplo:

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

Nesse caso, accessserá definido como WriteAccess.ReadWrite, pois WriteAccess Enumtem um valor máximo de 3.

Outro efeito colateral de misturar Enums com FlagsAttributee sem ele é que o processo de conversão não resultará em uma correspondência de 1 para 1 entre seus valores.

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

Nesse caso, lettersterá um valor de em Letters.Hvez de Letters.D, visto que o valor de apoio de Flavors.Peaché 8. Além disso, uma conversão de Flavors.Cherry | Flavors.Grapepara Lettersresultaria Letters.C, o que pode parecer não intuitivo.

Thick_propheT
fonte
2

Com base na resposta de Justin acima, eu vim com o seguinte:

    /// <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
            );
        }
    }
Sam Jazz
fonte
1

Eu sei que essa é uma pergunta antiga e tenho muitas respostas. No entanto, acho que usar uma instrução switch como a resposta aceita é um pouco complicado, então aqui estão meus 2 centavos:

Meu método favorito é usar um dicionário, onde a chave é o enum de origem e o valor é o enum de destino - portanto, no caso apresentado na pergunta, meu código seria assim:

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

Claro, isso pode ser empacotado em uma classe estática e usado como um método de extensão:

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

}
Zohar Peled
fonte
Gosto dessa abordagem, pois você também pode enumerar ambas as enumerações para preencher o dicionário. (quando eles estão na mesma ordem, é claro)
AlexS
0

Você pode usar ToString () para converter o primeiro enum em seu nome e, em seguida, Enum.Parse () para converter a string de volta no outro Enum. Isso lançará uma exceção se o valor não for compatível com o enum de destino (ou seja, para um valor "Desconhecido")

Jason Williams
fonte