Reputation: 5323
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?
Upvotes: 189
Views: 251014
Reputation: 71
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
Upvotes: 0
Reputation: 19
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 + "'");
}
}
Upvotes: 0
Reputation: 989
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.
Upvotes: 0
Reputation: 191
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:
MyGender
or TheirGender
.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:
Do:
Upvotes: 17
Reputation: 41
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($"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($"Invalid gender: {gender}")
};
}
Upvotes: 4
Reputation: 41
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;
}
Upvotes: 4
Reputation: 141
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
);
}
}
Upvotes: 4
Reputation: 341
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.
Upvotes: 27
Reputation: 82474
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;
}
}
Upvotes: 1
Reputation: 424
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>();
Upvotes: 7
Reputation: 1060
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 handles Enum
s with the FlagsAttribute
as well as Enum
s 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 Enum
s 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 Enum
s 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.
Upvotes: 3
Reputation: 14382
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());
}
Upvotes: 16
Reputation: 8008
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);
Upvotes: 67
Reputation: 11438
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.
Upvotes: 109
Reputation: 1062780
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)
Upvotes: 342
Reputation: 39437
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
}
}
Upvotes: 9
Reputation: 57902
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)
Upvotes: 0
Reputation: 5932
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.
Upvotes: 24