将枚举转换为另一种类型的枚举
我有一个枚举,例如“Gender
”(Male =0,Female =1
),并且我有另一个来自具有自己的 Gender 枚举的服务的枚举(男性=0,女性=1,未知=2
)
我的问题是我怎样才能写一些快速而漂亮的东西来从他们的枚举转换为我的?
I have an enum of for example 'Gender
' (Male =0 , Female =1
) and I have another enum from a service which has its own Gender enum (Male =0 , Female =1, Unknown =2
)
My question is how can I write something quick and nice to convert from their enum to mine?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
给定
Enum1 value = ...
,那么如果您的意思是名称:如果您的意思是数值,通常可以直接转换:(
通过转换,您可能需要使用
Enum. IsDefined
检查有效值,不过)Given
Enum1 value = ...
, then if you mean by name:If you mean by numeric value, you can usually just cast:
(with the cast, you might want to use
Enum.IsDefined
to check for valid values, though)当使用 Nate 建议的两种转换方法时,使用扩展方法的效果非常好:
显然,如果您不想,则无需使用单独的类。我的偏好是按照扩展方法所适用的类/结构/枚举进行分组。
Using an extension method works quite neatly, when using the two conversion methods suggested by Nate:
Obviously there's no need to use separate classes if you don't want to. My preference is to keep extension methods grouped by the classes/structures/enumerations they apply to.
只需将一个强制转换为 int,然后将其强制转换为另一个枚举(考虑到您希望基于值完成映射):
Just cast one to int and then cast it to the other enum (considering that you want the mapping done based on value):
如果我们有:
并且
我们可以安全地执行
或者甚至
如果您想涵盖“=”号右侧的枚举比左侧的枚举具有更多值的情况 - 您将必须编写自己的方法/dictionary 来涵盖其他人建议的内容。
If we have:
and
We can safely do
Or even
If you want to cover the case where an enum on the right side of the '=' sign has more values than the enum on the left side - you will have to write your own method/dictionary to cover that as others suggested.
为了彻底起见,我通常创建一对函数,一个接受 Enum 1 并返回 Enum 2,另一个接受 Enum 2 并返回 Enum 1。每个函数都包含一个将输入映射到输出的 case 语句,默认情况会引发异常,并带有抱怨意外值的消息。
在这种特殊情况下,您可以利用 Male 和 Female 的整数值相同的事实,但我会避免这种情况,因为它很糟糕,并且如果将来任一枚举发生变化,就会受到破坏。
To be thorough I normally create a pair of functions, one that takes Enum 1 and returns Enum 2 and another that takes Enum 2 and returns Enum 1. Each consists of a case statement mapping inputs to outputs and the default case throws an exception with a message complaining about an unexpected value.
In this particular case you could take advantage of the fact that the integer values of Male and Female are the same, but I'd avoid that as it's hackish and subject to breakage if either enum changes in the future.
我写这个答案是因为我相信已经提供的大多数答案都存在根本性问题,并且可以接受的答案并不完整。
通过枚举整数值映射
这种方法很糟糕,因为它假设
MyGender
和TheirGender
的整数值始终保持可比性。实际上,即使在单个项目中也很难保证这一点,更不用说单独的服务了。我们采用的方法应该可以用于其他枚举映射情况。我们永远不应该假设一个枚举与另一个枚举相同 - 特别是当我们可能无法控制其中一个或另一个时。
按枚举字符串值映射
这要好一点,因为即使整数表示形式发生更改,
MyGender.Male
仍会转换为TheirGender.Male
,但仍不理想。我不鼓励这种方法,因为它假设名称值不会改变,并且始终保持不变。考虑到未来的增强,您不能保证情况一定会如此;考虑是否添加了
MyGender.NotKnown
。您可能希望将其映射到TheirGender.Unknown
,但这不受支持。此外,假设一个枚举在名称上等同于另一个枚举通常是不好的,因为在某些情况下可能并非如此。如前所述,理想的方法适用于其他枚举映射要求。
显式映射枚举
此方法使用 switch 语句将
MyGender
显式映射到TheirGender
。这更好,因为:
MyGender
或TheirGender
的新枚举值。假设我们有以下枚举:
我们可以创建以下函数来“从他们的枚举转换为我的枚举”:
之前的答案建议返回一个可以为空的枚举(
TheirGender?
)并且对于任何不匹配的输入返回 null。这很糟糕; null 与未知映射不同。如果无法映射输入,则应抛出异常,否则应根据行为更明确地命名该方法:其他注意事项
如果可能在各个部分多次需要此方法对于解决方案,您可以考虑为此创建一个扩展方法:
如果您使用的是 C#8,则可以使用 开关表达式的语法 和 表达式主体 来整理代码:
如果您只在单个类中映射枚举,那么扩展方法可能有点过分了。在这种情况下,可以在类本身内声明该方法。
此外,如果映射仅发生在单个方法中,那么您可以将其声明为 本地函数:
这是一个 dotnet fiddle 链接,其中包含上面的例子。
tl;dr:
不要:
应做:
I wrote this answer because I believe there are fundamental issues with the majority of answers already provided, and the ones that are acceptable are incomplete.
Mapping by enum integer value
This approach is bad simply because it assumes that the integer values of both
MyGender
andTheirGender
will always remain comparable. In practice, it is very rare that you can guarantee this even within a single project, let alone a separate service.The approach we take should be something that can be used for other enum-mapping cases. We should never assume that one enum identically relates to another - especially when we may not have control over one or another.
Mapping by enum string value
This is a little better, as
MyGender.Male
will still convert toTheirGender.Male
even if the integer representation is changed, but still not ideal.I would discourage this approach as it assumes the name values will not change, and will always remain identical. Considering future enhancements, you cannot guarantee that this will be the case; consider if
MyGender.NotKnown
was added. It is likely that you would want this to map toTheirGender.Unknown
, but this would not be supported.Also, it is generally bad to assume that one enum equates to another by name, as this might not be the case in some contexts. As mentioned earlier, an ideal approach would work for other enum-mapping requirements.
Explicitly mapping enums
This approach explictly maps
MyGender
toTheirGender
using a switch statement.This is better as:
MyGender
orTheirGender
.Assuming we have the following enums:
We can create the following function to "convert from their enum to mine":
A previous answer suggested returning a nullable enum (
TheirGender?
) and returning null for any unmatched input. This is bad; null is not the same as an unknown mapping. If the input cannot be mapped, an exception should be thrown, else the method should be named more explictly to the behaviour:Additional considerations
If it is likely that this method will be required more than once in various parts of the solution, you could consider creating an extension method for this:
If you are using C#8, you can use the syntax for switch expressions and expression bodies to neaten up the code:
If you will only ever be mapping the enums within a single class, then an extension method may be overkill. In this case, the method can be declared within the class itself.
Furthermore, if the mapping will only ever take place within a single method, then you can declare this as a local function:
Here's a dotnet fiddle link with the above example.
tl;dr:
Do not:
Do:
您可以编写一个简单的通用扩展方法,如下所示
You could write a simple generic extension method like this
您可以编写一个简单的函数,如下所示:
you could write a simple function like the following:
如果有人感兴趣的话,这是一个扩展方法版本
Here's an extension method version if anyone is interested
根据上面贾斯汀的回答我想出了这个:
Based on Justin's answer above I came up with this:
如果枚举成员具有不同的值,您可以应用如下内容:
然后您可以调用:
var myGender = sex.MapToMyGender();
更新:
前面的代码仅适用于 C# 8。
对于旧版本的 C#,您可以使用 switch 语句而不是 switch 表达式:
In case when the enum members have different values, you can apply something like this:
Then you can call:
var myGender = gender.MapToMyGender();
Update:
This previous code works only with C# 8.
For older versions of C#, you can use the switch statement instead of the switch expression:
我不久前编写了一组扩展方法,适用于几种不同类型的
Enum
。其中一个特别适用于您想要完成的任务,并使用FlagsAttribute
处理Enum
以及具有不同基础类型的Enum
。从那里您可以添加其他更具体的扩展方法。
这将改变
Enum
的类型,就像您尝试做的那样。但请注意,您可以使用此方法在任何
Enum
和任何其他Enum
之间进行转换,即使是那些没有标志的方法。例如:变量
turtle
的值为Turtle.Blue
。但是,使用此方法可以避免未定义的
Enum
值。例如:在本例中,
access
将设置为WriteAccess.ReadWrite
,因为WriteAccess
Enum
有一个最大值为 3。将
Enum
与FlagsAttribute
和不带FlagsAttribute
混合使用的另一个副作用是,转换过程不会导致它们的值之间出现 1 比 1 的匹配。在这种情况下,
letters
的值将是Letters.H
而不是Letters.D
,因为支持值是Flavors。 Peach
是 8。另外,也是从Flavors.Cherry | 转换而来。 Flavors.Grape
到Letters
会产生Letters.C
,这看起来不直观。I wrote a set extension methods a while back that work for several different kinds of
Enum
s. One in particular works for what you are trying to accomplish and handlesEnum
s with theFlagsAttribute
as well asEnum
s with different underlying types.From there you can add other more specific extension methods.
This one will change types of
Enum
s like you are trying to do.Be warned, though, that you CAN convert between any
Enum
and any otherEnum
using this method, even those that do not have flags. For example:The variable
turtle
will have a value ofTurtle.Blue
.However, there is safety from undefined
Enum
values using this method. For instance:In this case,
access
will be set toWriteAccess.ReadWrite
, since theWriteAccess
Enum
has a maximum value of 3.Another side effect of mixing
Enum
s with theFlagsAttribute
and those without it is that the conversion process will not result in a 1 to 1 match between their values.In this case,
letters
will have a value ofLetters.H
instead ofLetters.D
, since the backing value ofFlavors.Peach
is 8. Also, a conversion fromFlavors.Cherry | Flavors.Grape
toLetters
would yieldLetters.C
, which can seem unintuitive.我知道这是一个老问题并且有很多答案,但是我发现在接受的答案中使用 switch 语句有点麻烦,所以这里是我的 2 美分:
我个人最喜欢的方法是使用字典,其中关键是源枚举,值是目标枚举 - 因此在问题中提出的情况下,我的代码将如下所示:
当然,这可以包装在静态类中并用作扩展方法:
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:
Of course, this can be wrapped in a static class and be used as an extension methods:
您可以使用 ToString() 将第一个枚举转换为其名称,然后使用 Enum.Parse() 将字符串转换回另一个枚举。如果目标枚举不支持该值(即“未知”值),这将引发异常
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)
对于笑容,你可以这样做
}
public class EnumConvertAttribute : Attribute {
公共枚举 TargetEnum;
}
称呼:
var t1 = TestNum.Test2;
var t2 = t1.GetEnum();
t2 = NewType.BType;
假设你不介意拳击,它会起作用。无论如何,大量转换枚举并不是一个好的做法。你确实得到了强类型,你没有两种转换方法最终在维护时创建错误,并且你不祈祷字符串或 int 比较不会失败。
For grins you can do this
}
public class EnumConvertAttribute : Attribute {
public Enum TargetEnum;
}
call:
var t1 = TestNum.Test2;
var t2 = t1.GetEnum();
t2 = NewType.BType;
It works, assuming you don't mind boxing. Converting enums a lot is not good practice anyways. You do get strong typing, you don't have 2 methods for conversions to create a bug eventually when maintaining, and your not praying that the string or int comparisons don't fail.
我的答案基于答案的
我编写了一个 nuget 库。
安装:
代码示例:
更多详细信息您可以在此处找到
My answer is based on answer of Mark.
I wrote a nuget library.
Installation:
Code example:
More details you can find here