我有一个例如'Gender
'(Male =0 , Female =1
)的枚举,我有一个来自服务的另一个枚举,它有自己的性别枚举(Male =0 , Female =1, Unknown =2
)
我的问题是我如何能够快速而愉快地编写从枚举转换为我的内容?
答案 0 :(得分:175)
给定Enum1 value = ...
,如果你的意思是名字:
Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());
如果你的意思是数值,你通常可以只是演员:
Enum2 value2 = (Enum2)value;
(使用强制转换,您可能希望使用Enum.IsDefined
来检查有效值,但是)
答案 1 :(得分:73)
当使用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
}
}
显然,如果你不愿意,不需要使用单独的类。我倾向于将扩展方法按照它们适用的类/结构/枚举进行分组。
答案 2 :(得分:44)
只需将一个转换为int,然后将其转换为另一个枚举(考虑到您希望根据值完成映射):
Gender2 gender2 = (Gender2)((int)gender1);
答案 3 :(得分:20)
要彻底,我通常会创建一对函数,一个接受Enum 1并返回Enum 2,另一个接受Enum 2并返回Enum 1.每个函数都包含一个case语句,将输入映射到输出,默认情况下抛出一个一条消息抱怨意外值的异常。
在这种特殊情况下,你可以利用男性和女性的整数值是相同的这一事实,但我会避免这种情况,因为如果任何枚举在将来发生变化,它就会破裂并且会受到破坏。
答案 4 :(得分:14)
如果我们有:
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;
如果你想覆盖' ='右侧的枚举的情况。 sign的值比左侧的枚举更多 - 你必须编写自己的方法/字典来覆盖其他人所建议的。
答案 5 :(得分: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());
}
答案 6 :(得分: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
}
}
答案 7 :(得分:5)
如果有人感兴趣,这是一个扩展方法版本
public static TEnum ConvertEnum<TEnum >(this Enum source)
{
return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
}
// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();
答案 8 :(得分:2)
我曾经写过一组扩展方法,可用于几种不同类型的Enum
。其中一个特别适用于您要完成的任务,并使用Enum
以及具有不同基础类型的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>();
}
在这种情况下,access
将设置为WriteAccess.ReadWrite
,因为WriteAccess
Enum
的最大值为3。
将Enum
与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>();
}
混合的另一个副作用是转换过程不会导致其值之间的1对1匹配。
letters
在这种情况下,Letters.H
的值为Letters.D
而不是Flavors.Peach
,因为Flavors.Cherry | Flavors.Grape
的支持值为8.此外,Letters
的转化1}}到Letters.C
会产生{{1}},这看似不直观。
答案 9 :(得分:2)
如果枚举成员具有不同的值,则可以应用如下内容:
public static MyGender? MapToMyGender(this Gender gender)
{
return gender switch
{
Gender.Male => MyGender.Male,
Gender.Female => MyGender.Female,
Gender.Unknown => null,
_ => throw new InvalidEnumArgumentException($"Invalid gender: {gender}")
};
}
然后您可以致电:var myGender = gender.MapToMyGender();
更新: 先前的代码仅适用于C#8。 对于旧版本的C#,可以使用switch语句代替switch表达式:
public static MyGender? MapToMyGender(this Gender gender)
{
switch (gender)
{
case Gender.Male:
return MyGender.Male;
case Gender.Female:
return MyGender.Female;
case Gender.Unknown:
return null;
default:
throw new InvalidEnumArgumentException($"Invalid gender: {gender}")
};
}
答案 10 :(得分: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;
}
}
答案 11 :(得分:1)
基于上面的Justin's答案,我想到了这一点:
/// <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
);
}
}
答案 12 :(得分:1)
select itype + '-' + convert(varchar(10), iyear) + '-' + convert(varchar(10), inum) from myTable
答案 13 :(得分:0)
您可以使用ToString()将第一个枚举转换为其名称,然后使用Enum.Parse()将字符串转换回另一个枚举。如果目标枚举不支持该值(即“未知”值),则会抛出异常
答案 14 :(得分:0)
我之所以写这个答案,是因为我认为已经提供的大多数答案都存在一些基本问题,而可以接受的答案是不完整的。
按枚举整数值映射
此方法之所以不好,是因为它假设MyGender
和TheirGender
的整数值始终保持可比性。实际上,即使在单个项目中也不能保证这一点,更不用说单独的服务了。
我们采用的方法应该可以用于其他枚举映射案例。我们永远不应假定一个枚举与另一个枚举完全相同,尤其是当我们可能无法控制一个枚举时。
按枚举字符串值映射
这会更好一些,因为即使更改了整数表示形式,MyGender.Male
仍将转换为TheirGender.Male
,但仍然不理想。
我不鼓励这种方法,因为它假定名称值不会改变,并且始终保持相同。考虑到将来的增强功能,您不能保证会如此。考虑是否添加了MyGender.NotKnown
。您可能希望它映射到TheirGender.Unknown
,但是不支持。
此外,通常很难假设一个枚举在名称上等同于另一个枚举,因为在某些情况下可能并非如此。如前所述,理想的方法将适用于其他枚举映射需求。
显式映射枚举
此方法使用switch语句将MyGender
明确映射到TheirGender
。
最好这样做:
MyGender
或TheirGender
的新枚举值。假设我们有以下枚举:
public enum MyGender
{
Male = 0,
Female = 1,
}
public enum TheirGender
{
Male = 0,
Female = 1,
Unknown = 2,
}
我们可以创建以下函数来“ 将其枚举转换为我的枚举”:
public MyGender GetMyGender(TheirGender theirGender)
{
switch (theirGender)
{
case TheirGender.Male:
return MyGender.Male;
case TheirGender.Female:
return MyGender.Female;
default:
throw new InvalidEnumArgumentException(nameof(theirGender), (int)theirGender, typeof(TheirGender));
}
}
先前的答案建议返回可为空的枚举(TheirGender?
),并为所有不匹配的输入返回null。这不好; null与未知映射不同。如果无法映射输入,则应引发异常,否则应将方法更明确地命名为行为:
public TheirGender? GetTheirGenderOrDefault(MyGender myGender)
{
switch (myGender)
{
case MyGender.Male:
return TheirGender.Male;
case MyGender.Female:
return TheirGender.Female;
default:
return default(TheirGender?);
}
}
其他注意事项
如果在解决方案的各个部分中可能需要多次使用此方法,则可以考虑为此创建扩展方法:
public static class TheirGenderExtensions
{
public static MyGender GetMyGender(this TheirGender theirGender)
{
switch (theirGender)
{
case TheirGender.Male:
return MyGender.Male;
case TheirGender.Female:
return MyGender.Female;
default:
throw new InvalidEnumArgumentException(nameof(theirGender), (int)theirGender, typeof(TheirGender));
}
}
}
如果您使用的是C#8,则可以使用syntax for switch expressions和expression bodies来整理代码:
public static class TheirGenderExtensions
{
public static MyGender GetMyGender(this TheirGender theirGender)
=> theirGender switch
{
TheirGender.Male => MyGender.Male,
TheirGender.Female => MyGender.Female,
_ => throw new InvalidEnumArgumentException(nameof(theirGender), (int)theirGender, typeof(TheirGender))
};
}
如果您将只在单个类中映射枚举,则扩展方法可能会过大。在这种情况下,可以在类本身中声明该方法。
此外,如果映射仅在单个方法中进行,则可以将其声明为local function:
public static void Main()
{
Console.WriteLine(GetMyGender(TheirGender.Male));
Console.WriteLine(GetMyGender(TheirGender.Female));
Console.WriteLine(GetMyGender(TheirGender.Unknown));
static MyGender GetMyGender(TheirGender theirGender)
=> theirGender switch
{
TheirGender.Male => MyGender.Male,
TheirGender.Female => MyGender.Female,
_ => throw new InvalidEnumArgumentException(nameof(theirGender), (int)theirGender, typeof(TheirGender))
};
}
Here's a dotnet fiddle link with the above example。
tl; dr:
不要:
要做: