Enum value as hidden in C# - 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;

Related

Default value in string property [duplicate]

I'm trying to make default value for my struct.
For example default value for Int - 0, for DateTime - 1/1/0001 12:00:00 AM.
As known we can't define parameterless constructor in structure.
struct Test
{
int num;
string str;
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(default(Test)); // shows namespace and name of struct test.Test
Console.WriteLine(new Test()); // same
Console.ReadKey(true);
}
}
How can I make a default value for struct?
You can't. Structures are always pre-zeroed, and there is no guarantee the constructor is ever called (e.g. new MyStruct[10]). If you need default values other than zero, you need to use a class. That's why you can't change the default constructor in the first place (until C# 6) - it never executes.
The closest you can get is by using Nullable fields, and interpreting them to have some default value if they are null through a property:
public struct MyStruct
{
int? myInt;
public int MyInt { get { return myInt ?? 42; } set { myInt = value; } }
}
myInt is still pre-zeroed, but you interpret the "zero" as your own default value (in this case, 42). Of course, this may be entirely unnecessary overhead :)
As for the Console.WriteLine, it simply calls the virtual ToString. You can change it to return it whatever you want.
Your problem is not with the behaviour of C#/.Net. The way you instantiate the struct effectively creates an instance with default values for all member fields.
The Console.WriteLine converts its argument to a string using the ToString() method. The default implementation (Object.ToString()) simply writes the fully qualified class name (namespace and name, as you call it).
If you want another visualisation, you should override the ToString method:
public struct Test
{
int num;
string str;
public override string ToString()
{
return $"num: {num} - str: {str}";
}
}
This is my take on this in case somebody finds it useful.
public struct MyStruct
{
public int item1;
public float item2;
public float item3;
public static MyStruct Null => new MyStruct(-1, 0, 0);
}
I have a static method inside my struct so that I can do this:
var data = MyStruct.Null;
instead of
var data = new MyStruct();
data.item1 = -1;
...
Or create a custom constructor to pass the data.
Printing out objects of the C# results with namespaces unless you override .ToString() for your objects. Can you define your struct like below and try it ?
public struct Test
{
int num;
string str;
public override string ToString()
{
return "Some string representation of this struct";
}
}
PS: default(Test) gives you a struct contains default(int) and default(string) which I mean Test.num is 0 and Test.str is null
Hope this helps
You can also do this:
public struct MyStruct
{
public static readonly Default = new MyStruct(42);
public int i;
public MyStruct(int i)
{
this.i = i;
}
}
And then when you create a default struct of this type do this:
public MyStruct newStruct = MyStruct.Default;
But of course, this won't override default and other programmers will bump their heads a few times. Really consider if a struct is the way to go, from the microsoft docs:
"A structure type (or struct type) is a value type that can encapsulate data and related functionality. Typically, you use structure types to design small data-centric types that provide little or no behavior."
Consider this: if you had 2 values in your struct and you wanted to make constructors, would 2 or less constructors suffice? If the answer is no, then the answer is: don't use a struct.
What you probably want to do is to override ToString(), e.g.
struct Test
{
int num;
string str;
public override string ToString ()
{
return string.Format ($"{str} | {num}");
}
}
As you have mentioned, it is impossible to define default values for fields other than default values for their appropriate types. However, with an overriden ToString(), you will see better formatted information about your structure in the console and during debugging.

How to return enum from property

Is there a way how to return full enum by property? Here is example of what I want to do:
// MyEnums.cs
public enum Languages
{
cs = 0,
pl = 1,
en = 2,
de = 3,
}
// General.cs
public static MyEnums.Languages Languages
{
get
{
return MyEnums.Languages;
}
}
enum is a type, i guess you actually want to get all enum-values. You could use this wrapper:
public static class EnumWrapper<T> where T : struct
{
public static T[] Values
{
get
{
Type ofT = typeof(T);
if (!ofT.IsEnum) throw new ArgumentException("Must be enum type");
return Enum.GetValues(ofT).Cast<T>().ToArray();
}
}
}
// ...
Languages[] languages = EnumWrapper<Languages>.Values;
If you want to return all available values defined in the enum, you can use
Enum.GetValues(typeof(MyEnums.Languages));
and modify your method so it returns a MyEnums.Languages[] (or a List<MyEnums.Languages>, which is always easier to manage for calling code)
To get all values in the enum, use Enum.GetValues. You might also want to cast it back to the correct type:
Languages[] languages = Enum.GetValues(typeof(Languages)).Cast<Languages>().ToArray();
// languages is an array containing { Languages.cs, Languages.pl, Languages.en, Languages.de }

What is the best way to implement a Rust enum in C#?

I have an entity that can be in one of different states (StateA, StateB and StateC), and in each of them have relevant data of distinct types (TStateA, TStateB, TStateC). Enums in Rust represent this perfectly. What is the best way to implement something like this in C#?
This question may appear similar, but enums in Rust and unions in C are significantly different.
You need a class to represent your Entity
class Entity {States state;}
Then you need a set of classes to represent your states.
abstract class States {
// maybe something in common
}
class StateA : MyState {
// StateA's data and methods
}
class StateB : MyState {
// ...
}
Then you need to write code like
StateA maybeStateA = _state as StateA;
If (maybeStateA != null)
{
- do something with the data in maybeStateA
}
C# does not have a nice way of writing code for this yet, maybe the Pattern Matching that is being considered for C#.next would help.
I think you should rethink your design to use object relationships and containment, trying to take a design that works in rust and force it into C# may not be the best option.
This might be crazy, but if you are hard-up about emulating Rust-like enums in C#, you could do it with some generics. Bonus: you keep type-safety and also get Intellisense out of the deal! You'll lose a little flexibility with various value types, but I think the safety is probably worth the inconvenience.
enum Option
{
Some,
None
}
class RustyEnum<TType, TValue>
{
public TType EnumType { get; set; }
public TValue EnumValue { get; set; }
}
// This static class basically gives you type-inference when creating items. Sugar!
static class RustyEnum
{
// Will leave the value as a null `object`. Not sure if this is actually useful.
public static RustyEnum<TType, object> Create<TType>(TType e)
{
return new RustyEnum<TType, object>
{
EnumType = e,
EnumValue = null
};
}
// Will let you set the value also
public static RustyEnum<TType, TValue> Create<TType, TValue>(TType e, TValue v)
{
return new RustyEnum<TType, TValue>
{
EnumType = e,
EnumValue = v
};
}
}
void Main()
{
var hasSome = RustyEnum.Create(Option.Some, 42);
var hasNone = RustyEnum.Create(Option.None, 0);
UseTheEnum(hasSome);
UseTheEnum(hasNone);
}
void UseTheEnum(RustyEnum<Option, int> item)
{
switch (item.EnumType)
{
case Option.Some:
Debug.WriteLine("Wow, the value is {0}!", item.EnumValue);
break;
default:
Debug.WriteLine("You know nuffin', Jon Snow!");
break;
}
}
Here's another sample demonstrating the use of a custom reference type.
class MyComplexValue
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
public override string ToString()
{
return string.Format("A: {0}, B: {1}, C: {2}", A, B, C);
}
}
void Main()
{
var hasSome = RustyEnum.Create(Option.Some, new MyComplexValue { A = 1, B = 2, C = 3});
var hasNone = RustyEnum.Create(Option.None, null as MyComplexValue);
UseTheEnum(hasSome);
UseTheEnum(hasNone);
}
void UseTheEnum(RustyEnum<Option, MyComplexValue> item)
{
switch (item.EnumType)
{
case Option.Some:
Debug.WriteLine("Wow, the value is {0}!", item.EnumValue);
break;
default:
Debug.WriteLine("You know nuffin', Jon Snow!");
break;
}
}
This looks a lot like Abstract Data Types in functional languages. There's no direct support for this in C#, but you can use one abstract class for the data type plus one sealed class for each data constructor.
abstract class MyState {
// maybe something in common
}
sealed class StateA : MyState {
// StateA's data and methods
}
sealed class StateB : MyState {
// ...
}
Of course, there's nothing prohibiting you from adding a StateZ : MyState class later, and the compiler won't warn you that your functions are not exhaustive.
Just from the back of my head, as a quick implementation...
I would first declare the Enum type and define enumerate items normally.
enum MyEnum{
[MyType('MyCustomIntType')]
Item1,
[MyType('MyCustomOtherType')]
Item2,
}
Now I define the Attribute type MyTypeAttribute with a property called TypeString.
Next, I need to write an extension method to extract the Type for each enum item (first in string, then later reflect to real type):
public static string GetMyType(this Enum eValue){
var _nAttributes = eValue.GetType().GetField(eValue.ToString()).GetCustomAttributes(typeof (MyTypeAttribute), false);
// handle other stuff if necessary
return ((MyTypeAttribute) _nAttributes.First()).TypeString;
}
Finally, get the real type using reflection...
I think the upside of this approach is easy to use later in the code:
var item = MyEnum.SomeItem;
var itemType = GetType(item.GetMyType());
I've been looking into Rust recently and been thinking the same questions. The real problem is the absence of the Rust deconstruction pattern matching but the type itself is long-winded but relatively straightforward if you are willing to use boxing:
// You need a new type with a lot of boilerplate for every
// Rust-like enum but they can all be implemented as a struct
// containing an enum discriminator and an object value.
// The struct is small and can be passed by value
public struct RustyEnum
{
// discriminator type must be public so we can do a switch because there is no equivalent to Rust deconstructor
public enum DiscriminatorType
{
// The 0 value doesn't have to be None
// but it must be something that has a reasonable default value
// because this is a struct.
// If it has a struct type value then the access method
// must check for Value == null
None=0,
IVal,
SVal,
CVal,
}
// a discriminator for users to switch on
public DiscriminatorType Discriminator {get;private set;}
// Value is reference or box so no generics needed
private object Value;
// ctor is private so you can't create an invalid instance
private RustyEnum(DiscriminatorType type, object value)
{
Discriminator = type;
Value = value;
}
// union access methods one for each enum member with a value
public int GetIVal() { return (int)Value; }
public string GetSVal() { return (string)Value; }
public C GetCVal() { return (C)Value; }
// traditional enum members become static readonly instances
public static readonly RustyEnum None = new RustyEnum(DiscriminatorType.None,null);
// Rusty enum members that have values become static factory methods
public static RustyEnum FromIVal(int i)
{
return new RustyEnum(DiscriminatorType.IVal,i);
}
//....etc
}
Usage is then:
var x = RustyEnum::FromSVal("hello");
switch(x.Discriminator)
{
case RustyEnum::DiscriminatorType::None:
break;
case RustyEnum::DiscriminatorType::SVal:
string s = x.GetSVal();
break;
case RustyEnum::DiscriminatorType::IVal:
int i = x.GetIVal();
break;
}
If you add some extra public const fields this could be reduced to
var x = RustyEnum::FromSVal("hello");
switch(x.Discriminator)
{
case RustyEnum::None:
break;
case RustyEnum::SVal:
string s = x.GetSVal();
break;
case RustyEnum::IVal:
int i = x.GetIVal();
break;
}
... but you then need a different name for creating the valueless members (like None in this example)
It seems to me that if the C# compiler was to implement rust enums without changing the CLR then this is the sort of code that it would generate.
It would be easy enough to create a .ttinclude to generate this.
Deconstruction is not as nice as Rust match but there is no alternative that is both efficient and idiot proof (the inefficient way is to use something like
x.IfSVal(sval=> {....})
To summarize my rambling - It can be done but it's unlikely to be worth the effort.
Short answer you can't. Even if you feel you can just don't do it you would shoot yourself in foot in doing so. We'll have to wait for the C# team to come up with a type with something like below
struct lives on stack in most cases this means it has a fixed size in memory
What we are expecting is sort of multiple struct with different layout but still fits in one decided stack of memory. The way rust handles this is by using the memory size of largest of the group for example
# Right now:
struct A { int a } # 4 bytes
struct B { int a, int b } # 8 bytes
# Can do but highly don't recommend would be waste of precious time, memory and cpu
struct AB {
A a,
B b
} # 12 bytes + 2 bytes to keep bool to check which struct should be used in code
# Future/Should be
super struct AB {
A(int),
B(int, int)
} # 8 bytes
Never did anything in Rust, but looking at the docs it seams to me that you would have to implement a textbook C# class. Since Rust enums even support functions and implementations of various types.
Probabily an abstract class.

Numeric constants in enum (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();

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