Numeric constants in enum (c#) - c#

I'm creating this selectbox in a SharePoint web part and need to have a drop down with the current version so I need to use an Enum.
public enum SelectVersionEnum { 2010, 2007 };
Well you can see where it breaks, is there any way to use integers in a enum?
Most of all I'd like to use
public enum SelectVersionEnum { 2010=14, 2007=12 };

No, you can not name enums with integer names.
An enum value name is a normal identifier and must follow the same rules as everything else.
You can, however, use:
public enum SelectVersionEnum
{
Version2007 = 12,
Version2010 = 14
}
Additionally, Enum.Parse can parse strings with integers into their corresponding enum value, even if value described in the string doesn't exist.
Try the following in LINQPad:
void Main()
{
Enum.Parse(typeof(SelectVersionEnum), "12").Dump();
Enum.Parse(typeof(SelectVersionEnum), "14").Dump();
Enum.Parse(typeof(SelectVersionEnum), "2007").Dump();
}
public enum SelectVersionEnum
{
Version2007 = 12,
Version2010 = 14
}
The output:
Version2007
Version2010
2007
What do you think would've happened if you defined the following:
public enum SelectVersionEnum
{
12 = 14,
14 = 16
}
Does the string "14" now mean "12" or "14"?

Enum members must be valid C# identifiers.
They cannot start with numbers.
Instead, you can use something like Office2007, Office2010 or V2007, V2010.

No, enum identifiers can't start with a numeric character.

You can add an extension method to your enum like any other type.
So you could create an extension for your SelectVersionEnum to help you get the enum values in the right format...
public static class SelectVersionEnumExtension
{
public static int Version(this SelectVersionEnum enumValue)
{
return 0; // Obviously you should return something meaningful here..
}
}
This gives you a lot of flexibilty.

One way you could have numeric values associated with enums is by using the description attribute. For example you might have the enum:
[Serializable]
public enum SelectVersionEnum
{
[Description("2007")]
v2007,
[Description("2010")]
v2010
}
You could then write an extension method to get the numeric value you are looking for.
public static string Description(this Enum value)
{
var type = value.GetType();
var name = Enum.GetName(type, value);
if (name != null)
{
if (type.GetField(name) != null)
{
var attr = Attribute.GetCustomAttribute(type.GetField(name), typeof(DescriptionAttribute)) as DescriptionAttribute;
return attr != null ? attr.Description : name;
}
}
return null;
} // end
You would use it like this:
var version = SelectVersionEnum.v2007.Description();

Related

Enum item mapped to another value

I have enum:
enum MyEnum{
aaaVal1,
aaaVal2,
aaaVal3,
}
I need to have abbreviated version of 'MyEnum' which maps every item from 'MyEnum' to different values. My current approach is method which simply translates every item:
string translate(MyEnum myEnum)
{
string result = "";
switch ((int)myEnum)
{
0: result = "abc";
1: result = "dft";
default: result = "fsdfds"
}
return result;
}
the problem with this approach is that every time programmer changes MyEnum he should also change translate method.
This is not a good way of programming.
So..
Is there any more elegant solution for this problem?
Thank you :-)
Four options:
Decorate your enum values with attributes, e.g.
enum MyEnum
{
[Description("abc")]
AaaVal1,
[Description("dft")]
AaaVal2,
AaaVal3,
}
Then you can create a mapping (like the dictionary solution below) via reflection.
Keep the switch statement but switch on the enum value instead of a number for better readability:
switch (myEnum)
{
case MyEnum.AaaVal1: return "abc";
case MyEnum.AaaVal2: return "dft";
default: return "fsdfds";
}
Create a Dictionary<MyEnum, string>:
private static Dictionary<MyEnum, string> EnumDescriptions =
new Dictionary<MyEnum, string>
{
{ MyEnum.AaaVal1, "abc" },
{ MyEnum.AaaVal2, "dft" },
};
You'd need to handle the defaulting in the method, of course.
Use a resource file, with an entry for each string representation. This would be better if you're really trying to translate in a way that might need different translations for different cultures.
Considering that the use of descriptors on enums is quite common, here it's a good-enough class to do it:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
class EnumDescriptor : Attribute
{
public readonly string Description;
public EnumDescriptor(string description)
{
this.Description = description;
}
public static string GetFromValue<T>(T value) where T : struct
{
var type = typeof(T);
var memInfo = type.GetField(value.ToString());
var attributes = memInfo.GetCustomAttributes(typeof(EnumDescriptor), false);
if (attributes.Length == 0)
{
return null;
}
return ((EnumDescriptor)attributes[0]).Description;
}
}
enum MyEnum
{
[EnumDescriptor("Hello")]
aaaVal1,
aaaVal2,
aaaVal3,
}
string translate(MyEnum myEnum)
{
// The ?? operator returns the left value unless the lv is null,
// if it's null it returns the right value.
string result = EnumDescriptor.GetFromValue(myEnum) ?? "fsdfds";
return result;
}
I'm finding what you're trying to do a bit weird.
If you're making translations, then you should create a RESX file and create ACTUAL translations.
But to answer your question, I guess you could create another enum with the same amount of fields and same numbering (if you're using anything other than the default) and have that act as the abbreviated names. Connecting one to the other should be straightforward:
string GetAbbreviation(Enum1 enum1)
{
return ((Enum2)((int)enum1)).ToString();
}
Attributes will be nice solution for this case. You can specify translations for enumeration members via declarative way:
public class TranslateAttribute
{
public string Translation { get; private set; }
public TranslateAttribute(string translation)
{
Translation = translation;
}
}
enum MyEnum
{
[Translate("abc")]
aaaVal1,
[Translate("dft")]
aaaVal2,
[Translate("fsdfds")]
aaaVal3
}
After this you should write common method for obtaining translations. It should check attribute with translation (via reflection) and return translation if it was specified and default value in other cases.

Can I make an Enum return something other than an integer?

If I have the following:
public enum TYPE
{
One = 1,
Two = 2,
Three = 3
}
When I do:
var a = TYPE.One;
I would like it to populate the variable a with a string in the format "01". In other words two digits with a leading zero.
Is it possible to do this by assigning some method to the SomeEnum? I realized I could use TYPE.One.ToString("00")but I would like to have it self-contained in the enum and something very simple to use.
You can add a string to each enum element using the description attribute.
e.g.
public Enum MyEnum
{
[Description("Value A Description")]
ValueA,
[Description[("Value B Description")]
ValueB
}
To retrieve the description value, use an extender class
public static class MyEnumExtender
{
public static string Description(this Enum Value)
{
FieldInfo FI = Value.GetType().GetField(Value.ToString());
IEnumerable<DescriptionAttribute> Attributes = FI.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>();
return (Attributes.Any()) ? Attributes.First().Description : Value.ToString();
}
}
....
MyEnum EnumVar = MyEnum.ValueA;
string Description = EnumVar.Description();
can do something like this :
public static class Ext {
public static string ToMyString(this Enumer en ) {
return ((int)en).ToString("00");
}
}
and after use this like:
public enum TYPE { One = 1, Two = 2, Three = 3 }
Type t = TYPE.One;
string s = t.ToMyString();
Yes, conceptually it's the same as like declaring a string , but it's hidden inside extension method.
Other solution is: to simply avoid, at this point, using enums in that way.
Don't use enums for that, but something like a Dictionary or Hash. Enums are there when there is a limited set of possibilities and you do not want or need a value. How it is stored is irrelevant.

C# Enum return string with breaks

I need my enum to return a specific string, but I can't work out how to make it return a string with breaks in it without having a method to do the conversion. Is it possible to make LicenseTypes.DISCOUNT_EARLY_ADOPTER return DISCOUNT EARLY-ADOPTER without the helper method?
// All license types
public enum LicenseTypes
{
DISCOUNT,
DISCOUNT_EARLY_ADOPTER,
COMMERCIAL,
COMMERCIAL_EARLY_ADOPTER
}
// Convert enum to correct string
public static string LicenseTypeToString(LicenseTypes Enum)
{
if (Enum == LicenseTypes.COMMERCIAL)
return "COMMERCIAL";
else if (Enum == LicenseTypes.COMMERCIAL_EARLY_ADOPTER)
return "COMMERCIAL EARLY-ADOPTER";
else if (Enum == LicenseTypes.DISCOUNT)
return "DISCOUNT";
else if (Enum == LicenseTypes.DISCOUNT_EARLY_ADOPTER)
return "DISCOUNT EARLY-ADOPTER";
else
return "ERROR";
}
Firstly, a separate option from a helper method is simply to have a Dictionary<LicenseTypes, string> which you populate once. That would probably be the simplest approach, to be honest:
private static readonly Dictionary<LicenseTypes, string> LicenseDesciptions =
new Dictionary<LicenseTypes, string>
{
{ LicenseTypes.COMMERCIAL, "COMMERCIAL" },
{ LicenseTypes.COMMERCIAL_EARLY_ADOPTER, "COMMERCIAL EARLY-ADOPTER" },
{ LicenseTypes.DOMESTIC, "DOMESTIC" },
{ LicenseTypes.DOMESTIC_EARLY_ADOPTER, "DOMESTIC EARLY-ADOPTER" },
};
(As noted in comments, another alternative is a switch/case... but I personally prefer this way, as effectively you've got a data mapping, so it makes sense to use a data structure rather than an execution flow structure. It also means you can swap out dictionaries for different languages etc if you want.)
Secondly, one option would be to decorate each enum value with a [Description] attribute (or your own attribute if you want), and find that out with reflection - Unconstrained Melody has an extension method which can do that very easily:
// Throws ArgumentOutOfRangeException if the licenseType value isn't defined
// or doesn't have a description.
string description = licenseType.GetDescription();
Also, following .NET naming conventions it should be:
public enum LicenseType // Singular as it's not a Flags enum
{
Discount,
DiscountEarlyAdopter,
Commercial,
CommercialEarlyAdopter
}
A little reflection, and Attribute magic, and this should just answer it :)
Getting attributes of Enum's value
Might also make it an extension method.
here's how it should look (also added generics):
public static class MyExtensionsClass
{
public static string ToDescriptionString<T>(this T val)
where T : struct, IConvertible
{
if (typeof(T).IsEnum)
{
var type = val.GetType();
var memInfo = type.GetMember(val.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
return ((DescriptionAttribute)attributes[0]).Description;
}
return ""; //all paths must return a value
}
}
public enum LicenseTypes
{
[Description("DISCOUNT")]
DISCOUNT,
[Description("DISCOUNT EARLY-ADOPTER")]
DISCOUNT_EARLY_ADOPTER,
[Description("COMMERCIAL")]
COMMERCIAL,
[Description("COMMERCIAL EARLY-ADOPTER")]
COMMERCIAL_EARLY_ADOPTER
}
Thank you, guys that wrote these:
Enhance enums using extension methods
Create Generic method constraining T to an Enum
Good luck!
While it doesn't eliminate the helper method, note that in your case you could just special-case the values that you can't get using ToString:
switch (Enum) {
case LicenseTypes.COMMERCIAL_EARLY_ADOPTER:
return "COMMERCIAL EARLY-ADOPTER";
case LicenseTypes.DISCOUNT_EARLY_ADOPTER:
return "DISCOUNT EARLY-ADOPTER";
default
return Enum.ToString();
}
I use this from Google's dotnet client - StringValueAttribute.cs
and Utilities.cs
public enum LicenseType
{
[StringValue("DISCOUNT")] Discount,
[StringValue("DISCOUNT EARLY-ADOPTER")] DiscountEarlyAdopter,
[StringValue("COMMERCIAL")] Commercial,
[StringValue("COMMERCIAL EARLY-ADOPTER")] CommercialEarlyAdopter
}
Then you can simply do this:
licenseType.GetStringValue();
Convert to string using "G" format, then replace "_" (underscore) with spaces:
LicensceTypes license = LicenseTypes.COMMERCIAL_EARLY_ADOPTERS;
string licenseDescription = license.ToString("G").Replace('_', ' ');
// licenseDescription = "COMMERCIAL EARLY ADOPTERS"
I think I would use a class to avoid this scenario. :/
public class LicenceType
{
private string name;
public LicenceType(string Name)
{
this.name = Name;
}
public override string ToString()
{
return name;
}
}
public static class LicenceTypes
{
public static LicenceType DISCOUNT = new LicenceType("DISCOUNT");
public static LicenceType DISCOUNT_EARLY_ADOPTER= new LicenceType("DISCOUNT EARLY-ADOPTER");
public static LicenceType COMMERCIAL= new LicenceType("COMMERCIAL");
public static LicenceType COMMERCIAL_EARLY_ADOPTER= new LicenceType("COMMERCIAL EARLY-ADOPTER");
}

Enum value as hidden in C#

I have an enum and i want to "hide" one of its values (as i add it for future support).
The code is written in C#.
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
Reserved
}
I don't want to allow peoples who use this code to use this values (MyEnum.Reserved).
Any idea?
TIA
You could use the 'Obsolete' attribute - semantically incorrect, but it will do what you want:
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
[Obsolete("Do not use this", true)]
Reserved
}
Anyone who tries to compile using the Foo.Reserved item will get an error
If you don't want to show it, then don't include it:
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
}
Note that a user of this enum can still assign any integer value to a variable declared as MyEnum:
MyEnum e = (MyEnum)2; // works!
This means that a method that accepts an enum should always validate this input before using it:
void DoIt(MyEnum e)
{
if (e != MyEnum.ValueA && e != MyEnum.ValueB)
{
throw new ArgumentException();
}
// ...
}
So, just add your value later, when you need it, and modify your methods to accept it then.
This is not possible in C#. All enum values are accessible if the Enum itself is accessible.
The only way you could simulate accomplishing this is by using a less accessible static field that used an integer value not already used in the Enum.
public enum MyEnum {
ValueA = 0;
ValueB = 1;
}
internal static class MyEnumEx {
internal static MyEnum Reserved = (MyEnum)42;
}
I'm curious though as to why you would want to do this. No matter what you do users can still provide the Reserved value. All that needs to be done is to cast an int of the appropriate value to the MyEnum type.
// Code that shouldn't access Reserve
MyEnum reserved = (MyEnum)42; // Woot!
if you want to hide from intellisense or PropertyGrid enumerated members, you can apply:
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
and
[System.ComponentModel.Browsable(false)]
Example:
public enum MyEnum
{
A,
B,
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
C
}
C is not visible
You can achieve something like this by using your own custom type instead of enum:
// This type works pretty much the same way an enum works;
// each specific value can be cast to/from an int, and each has
// a specific name that is returned on calling ToString().
public sealed class MyEnum
{
private readonly int _value;
private readonly string _name;
// private constructor -- ensure that the static members you define below
// are the only MyEnum instances accessible from any outside code
private MyEnum(int value, string name)
{
_value = value;
_name = name;
}
// no need to override Equals or GetHashCode, believe it or not --
// one instance per value means we can use reference equality and
// that should be just fine
public override string ToString()
{
return _name;
}
// provide direct access only to these members
public static readonly MyEnum ValueA = new MyEnum(0, "ValueA");
public static readonly MyEnum ValueB = new MyEnum(1, "ValueB");
// this member is only available to you within the current assembly
internal static readonly MyEnum Reserved = new MyEnum(-1, "Reserved");
}
You could even further emulate the behavior of enum values by, for example, overloading the explicit operators to convert to/from MyEnum objects to int values (took JaredPar's suggestion to use this rather than implicit):
public static explicit operator MyEnum(int value)
{
switch (value)
{
case 0:
return ValueA;
case 1:
return ValueB;
default:
throw new InvalidCastException();
}
}
public static explicit operator int(MyEnum value)
{
return value._value;
}
You can't hide it, but you can add a comment that this value doesn't have any effect at this moment. Otherwise just remove it and add it if you add the support, this shouldn't break any other code dependent on it, as long as the original values don't change.
Are you using the "hidden" value internally? If not:
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
//TODO: Reserved
}
You gain nothing by defining an unused variable.
one way you can do this set the value of this variable null.
so when ever its called from enum it'll b null. in short user can't access its value.
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
Reserved.None
}
I had a similar problem with enum Flags, but the solution I used should work without the Flags attribute.
You can create two enum types, one for the reserved values and the other for the publicly usable values. You can use HasFlag with the reserved type without casting, but assignment requires casting.
[Flags]
public enum MyEnumReserved {
Income = 1,
Expense = 2,
Discretionary = 4,
Critical = 8
}
[Flags]
public enum MyEnum {
Income = MyEnumReserved.Income,
DiscretionaryExpense = MyEnumReserved.Expense | MyEnumReserved.Discretionary,
CriticalExpense = MyEnumReserved.Expense | MyEnumReserved.Critical
}
bool IsIncome(MyEnum val) => val.HasFlag(MyEnumReserved.Income);
...
MyEnum foo = (MyEnum)MyEnumReserved.Expense | (MyEnum)MyEnumReserved.Discretionary;

convert an enum to another type of enum

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?
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)
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.
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);
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.
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.
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());
}
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
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
}
}
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>();
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}")
};
}
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
);
}
}
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;
}
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.
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;
}
}
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)
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.
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 + "'");
}
}

Categories

Resources