将枚举转换为另一种类型的枚举

发布于 2024-08-13 03:07:37 字数 172 浏览 3 评论 0原文

我有一个枚举,例如“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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

柒夜笙歌凉 2024-08-20 03:07:37

给定 Enum1 value = ...,那么如果您的意思是名称:

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

如果您的意思是数值,通常可以直接转换:(

Enum2 value2 = (Enum2)value;

通过转换,您可能需要使用 Enum. IsDefined 检查有效值,不过)

Given Enum1 value = ..., then if you mean by name:

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

If you mean by numeric value, you can usually just cast:

Enum2 value2 = (Enum2)value;

(with the cast, you might want to use Enum.IsDefined to check for valid values, though)

临走之时 2024-08-20 03:07:37

当使用 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
    }
}

显然,如果您不想,则无需使用单独的类。我的偏好是按照扩展方法所适用的类/结构/枚举进行分组。

Using an extension method works quite neatly, when using the two conversion methods suggested by 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
    }
}

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.

预谋 2024-08-20 03:07:37

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

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

Just cast one to int and then cast it to the other enum (considering that you want the mapping done based on value):

Gender2 gender2 = (Gender2)((int)gender1);
嘴硬脾气大 2024-08-20 03:07:37

如果我们有:

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;

如果您想涵盖“=”号右侧的枚举比左侧的枚举具有更多值的情况 - 您将必须编写自己的方法/dictionary 来涵盖其他人建议的内容。

If we have:

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

and

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

We can safely do

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

Or even

var enumOfGender2Type = (Gender2)0;

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.

酷炫老祖宗 2024-08-20 03:07:37

为了彻底起见,我通常创建一对函数,一个接受 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.

╭ゆ眷念 2024-08-20 03:07:37

我写这个答案是因为我相信已经提供的大多数答案都存在根本性问题,并且可以接受的答案并不完整。

通过枚举整数值映射

这种方法很糟糕,因为它假设 MyGenderTheirGender 的整数值始终保持可比性。实际上,即使在单个项目中也很难保证这一点,更不用说单独的服务了。

我们采用的方法应该可以用于其他枚举映射情况。我们永远不应该假设一个枚举与另一个枚举相同 - 特别是当我们可能无法控制其中一个或另一个时。

按枚举字符串值映射

这要好一点,因为即使整数表示形式发生更改,MyGender.Male 仍会转换为 TheirGender.Male ,但仍不理想。

我不鼓励这种方法,因为它假设名称值不会改变,并且始终保持不变。考虑到未来的增强,您不能保证情况一定会如此;考虑是否添加了 MyGender.NotKnown。您可能希望将其映射到 TheirGender.Unknown,但这不受支持。

此外,假设一个枚举在名称上等同于另一个枚举通常是不好的,因为在某些情况下可能并非如此。如前所述,理想的方法适用于其他枚举映射要求。

显式映射枚举

此方法使用 switch 语句将 MyGender 显式映射到 TheirGender

这更好,因为:

  • 涵盖基础整数值更改的情况。
  • 涵盖枚举名称更改的情况(即没有假设 - 开发人员将需要更新代码来处理该场景 - 很好)。
  • 处理枚举值无法映射的情况。
  • 处理添加新枚举值并且默认情况下无法映射的情况(同样,没有做出任何假设 - 很好)。
  • 可以轻松更新以支持 MyGenderTheirGender 的新枚举值。
  • 对于所有枚举映射要求可以采用相同的方法。

假设我们有以下枚举:

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,则可以使用 开关表达式的语法表达式主体 来整理代码:

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

如果您只在单个类中映射枚举,那么扩展方法可能有点过分了。在这种情况下,可以在类本身内声明该方法。

此外,如果映射仅发生在单个方法中,那么您可以将其声明为 本地函数:

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

这是一个 dotnet fiddle 链接,其中包含上面的例子

tl;dr:

不要:

  • 按整数值映射枚举
  • 按名称映射枚举

应做:

  • 使用 switch 语句显式映射枚举
  • 当值无法映射时抛出异常而不是返回 null
  • 考虑使用扩展方法

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 and TheirGender 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 to TheirGender.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 to TheirGender.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 to TheirGender using a switch statement.

This is better as:

  • Covers the case where the underlying integer value changes.
  • Covers the case where the enum names changes (i.e. no assumptions - the developer will need to update the code to handle the scenario - good).
  • Handles cases where enum values cannot be mapped.
  • Handles cases where new enum values are added and cannot be mapped by default (again, no assumptions made - good).
  • Can easily be updated to support new enum values for either MyGender or TheirGender.
  • The same approach can be taken for all enum mapping requirements.

Assuming we have the following enums:

public enum MyGender
{
    Male = 0,
    Female = 1,
}

public enum TheirGender
{
    Male = 0,
    Female = 1,
    Unknown = 2,
}

We can create the following function to "convert from their enum to mine":

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

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:

public TheirGender? GetTheirGenderOrDefault(MyGender myGender)
{
    switch (myGender)
    {
        case MyGender.Male:
            return TheirGender.Male;
            
        case MyGender.Female:
            return TheirGender.Female;
            
        default:
            return default(TheirGender?);
    }
}

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:

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

If you are using C#8, you can use the syntax for switch expressions and expression bodies to neaten up the code:

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

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:

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:

Do not:

  • Map enums by integer value
  • Map enums by name

Do:

  • Map enums explicitly using a switch statement
  • Throw an exception when a value cannot be mapped rather than returning null
  • Consider using extension methods
你怎么这么可爱啊 2024-08-20 03:07:37

您可以编写一个简单的通用扩展方法,如下所示

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

You could write a simple generic extension method like this

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());
}
笑看君怀她人 2024-08-20 03:07:37

您可以编写一个简单的函数,如下所示:

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

you could write a simple function like the following:

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
    }
}
唯憾梦倾城 2024-08-20 03:07:37

如果有人感兴趣的话,这是一个扩展方法版本

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

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();

Here's an extension method version if anyone is interested

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

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();
嗼ふ静 2024-08-20 03:07:37

根据上面贾斯汀的回答我想出了这个:

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

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
            );
        }
    }
_失温 2024-08-20 03:07:37
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;
}
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;
}
戏舞 2024-08-20 03:07:37

如果枚举成员具有不同的值,您可以应用如下内容:

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 = sex.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}")
    };
}

In case when the enum members have different values, you can apply something like this:

public static MyGender? MapToMyGender(this Gender gender)
{
    return gender switch
    {
        Gender.Male => MyGender.Male,
        Gender.Female => MyGender.Female,
        Gender.Unknown => null,
        _ => throw new InvalidEnumArgumentException(
quot;Invalid gender: {gender}")
    };
}

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:

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(
quot;Invalid gender: {gender}")
    };
}
乙白 2024-08-20 03:07:37

我不久前编写了一组扩展方法,适用于几种不同类型的Enum。其中一个特别适用于您想要完成的任务,并使用 FlagsAttribute 处理 Enum 以及具有不同基础类型的 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。

EnumFlagsAttribute 和不带 FlagsAttribute 混合使用的另一个副作用是,转换过程不会导致它们的值之间出现 1 比 1 的匹配。

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。 Peach 是 8。另外,也是从 Flavors.Cherry | 转换而来。 Flavors.GrapeLetters 会产生 Letters.C,这看起来不直观。

I wrote a set extension methods a while back that work for several different kinds of Enums. One in particular works for what you are trying to accomplish and handles Enums with the FlagsAttribute as well as Enums with different underlying types.

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

From there you can add other more specific extension methods.

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

This one will change types of Enums like you are trying to do.

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.

日裸衫吸 2024-08-20 03:07:37

我知道这是一个老问题并且有很多答案,但是我发现在接受的答案中使用 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;
    }

}

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

}
小伙你站住 2024-08-20 03:07:37

您可以使用 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)

自我难过 2024-08-20 03:07:37

对于笑容,你可以这样做

public enum TestNum {
{
    [EnumConvert( NewType.AType )]
    Test1,

    [EnumConvert( NewType.BType )]
    Test2
}

public enum NewType {
    AType,
    BType
}

public static class EnumExtensions {
public static Enum GetEnum(this Enum value ) {
    var attribute = (EnumConvertAttribute)value.GetType()
        .GetField( value.ToString() )
        .GetCustomAttribute( false )
        .Where( a => a is EnumConvertAttribute
        .FirstOrDefault();

    if( attribute == null ) {
        throw new ArgumentNullException();
    }

    try {
        return attribute.TargetEnum;
    } catch ( Exception ex ) {
        throw new InvalidArgumentException();
    }
}

}

public class EnumConvertAttribute : Attribute {
公共枚举 TargetEnum;

public EnumConvertAttribute( object e ) {
    TargetEnum = (Enum)e;
}

}

称呼:
var t1 = TestNum.Test2;
var t2 = t1.GetEnum();

t2 = NewType.BType;

假设你不介意拳击,它会起作用。无论如何,大量转换枚举并不是一个好的做法。你确实得到了强类型,你没有两种转换方法最终在维护时创建错误,并且你不祈祷字符串或 int 比较不​​会失败。

For grins you can do this

public enum TestNum {
{
    [EnumConvert( NewType.AType )]
    Test1,

    [EnumConvert( NewType.BType )]
    Test2
}

public enum NewType {
    AType,
    BType
}

public static class EnumExtensions {
public static Enum GetEnum(this Enum value ) {
    var attribute = (EnumConvertAttribute)value.GetType()
        .GetField( value.ToString() )
        .GetCustomAttribute( false )
        .Where( a => a is EnumConvertAttribute
        .FirstOrDefault();

    if( attribute == null ) {
        throw new ArgumentNullException();
    }

    try {
        return attribute.TargetEnum;
    } catch ( Exception ex ) {
        throw new InvalidArgumentException();
    }
}

}

public class EnumConvertAttribute : Attribute {
public Enum TargetEnum;

public EnumConvertAttribute( object e ) {
    TargetEnum = (Enum)e;
}

}

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.

篱下浅笙歌 2024-08-20 03:07:37
Enum1 var1;
Enum2 var2;

var2 = Enum2.fromValue(var1.getValue())

public enum Enum2 {
    FEMALE("FEMALE"),
    MALE("MALE")
    private String value;

    public static Enum2 fromValue(String value) {
        for (Enum2 b : Enum2.values()) {
            if (b.value.equals(value)) {
                return b;
            }
        }
        throw new IllegalArgumentException("Unexpected value '" + value + "'");
    }
}
Enum1 var1;
Enum2 var2;

var2 = Enum2.fromValue(var1.getValue())

public enum Enum2 {
    FEMALE("FEMALE"),
    MALE("MALE")
    private String value;

    public static Enum2 fromValue(String value) {
        for (Enum2 b : Enum2.values()) {
            if (b.value.equals(value)) {
                return b;
            }
        }
        throw new IllegalArgumentException("Unexpected value '" + value + "'");
    }
}
夜未央樱花落 2024-08-20 03:07:37

我的答案基于答案

我编写了一个 nuget 库

安装:

dotnet add package Kurnakov.EnumConverter

代码示例:

var anotherGenderEnum = genderEnum.ToAnother<AnotherGenderEnum>();

更多详细信息您可以在此处找到

My answer is based on answer of Mark.

I wrote a nuget library.

Installation:

dotnet add package Kurnakov.EnumConverter

Code example:

var anotherGenderEnum = genderEnum.ToAnother<AnotherGenderEnum>();

More details you can find here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文