将枚举转换为另一种枚举


120

我有一个枚举,例如' Gender'(Male =0 , Female =1),我还有一个服务具有自己的性别枚举(Male =0 , Female =1, Unknown =2)的另一个枚举

我的问题是我该如何快速而又好地将它们的枚举转换为我的枚举?


6
您要将“未知”转换为什么?
帕维尔·米纳夫

如果两个枚举类型的值相同,则可以将其枚举转换为其他枚举类型,请参见ideone.com/7lgvgf
Gowtham S

Answers:


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.TryparseEnum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; 这将使您可以处理不存在的输入值,Enum2而无需调用Enum.IsDefined或捕获ArgumentException由抛出的s Enum.Parse。请注意,参数的顺序与或多或少相反Enum.Parse
桑德

47

只需将一个转换为int然后将其转换为另一个枚举(考虑到您希望基于值完成映射):

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

3
尽管不太可能在野外看到它,也不太可能出现性别问题,但可能存在一些枚举由long(或ulong)支持,而不是由int定义在上方int.MaxValue(或下方)的成员支持int.MinValue),在这种情况下,强制转换为int可能会溢出,最终您会得到一个未定义的枚举值,该值应该定义。
Rich O'Kelly

当然。正确的方法是(Gender2)((在此处插入基础类型)gender1),但是我认为上面的示例给出了正确的想法,所以我不会更改它。
Adrian Zanescu

3
这要求两个枚举以相同的顺序具有相同的值。虽然它解决了这个特定的问题,但这确实很脆弱,我一般不会将其用于枚举映射。
sonicblis 2013年

2
好吧... 。映射需要基于某些东西来完成。在这种情况下,映射为整数值。对于基于名称的映射,您需要其他代码。对于另一种映射,还有其他东西。没有人说这是“一般的映射”,除非您可以尝试指定“一般的映射”是什么意思,否则这种情况就不存在
Adrian Zanescu

20

透彻地讲,我通常创建一对函数,一个函数使用Enum 1并返回Enum 2,另一个函数使用Enum 2并返回Enum1。每个函数都包含一个case语句,将输入映射到输出,默认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)。
贾斯汀

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。一个特别的作品要尝试什么来完成和手柄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像您尝试的那样更改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>();
}

在这种情况下,access将设为WriteAccess.ReadWrite,因为的WriteAccess Enum最大值为3。

Enums与FlagsAttribute和混合的另一种副作用是,转换过程不会导致s与它们之间的一对一匹配。

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.Peach8。此外,从Flavors.Cherry | Flavors.Grape到的转换Letters将产生Letters.C,这似乎是不直观的。


2

基于以上贾斯汀的回答,我想到了:

    /// <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()将字符串转换回另一个Enum。如果目标枚举不支持该值(例如,“未知”值),则会抛出异常

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.