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