How to convert any int into any generic Enum in C#? - c#

Because Enums aren't guaranteed to be of type int...
public enum ExampleEnum : int / ulong / whatever
You cannot do this with enums:
int i = (int)exampleEnum;
However in my case, while I don't know the exact 'type of enum' being used, I can guarantee that it will be a "int type" of enum.
So I can do this:
int i = Convert.ToInt32(exampleEnum);
Cool. Here's the problem: I don't know of a way to do the inverse (which I need to do), as:
Enum exampleEnum = (Enum)exampleEnum;
has error:
Cannot cast expression of type 'int' to type 'Enum'
And I cannot find an inverse of Convert.ToInt32(Enum enum)
That is the question, if you think more detail on what I'm trying to do is useful, I can provide you with it. But in a nutshell I am creating a generic GUI method that takes in any type of Enum:
public static int EditorPrefEnumField(string label, Enum defaultValue, string editorPrefString)
and getting it to work (the way I want) involves converting the Enum to and from an int.

You can use Enum.ToObject() method. You need to specify the actual enum type for that. Here is a generic method to encapsulate Enum.ToObject()
public enum TestEnum : int
{
A=1, B=2, C=3
};
public T GetEnumFromInt<T>(int value) where T : Enum
{
return (T)Enum.ToObject(typeof(T), value);
}
private void button1_Click(object sender, EventArgs e)
{
Enum value = GetEnumFromInt<TestEnum>(2);
MessageBox.Show(value.ToString()); // Displays "B"
}
You need to specify the concrete type of Enum you want because Enum is an abstract type and one cannot create instances of abstract types.

Take this code where I store an error code as an Int:
public class CommonError
{
public int Code { get; set; }
public CommonError FromErrorCode(Enum code, string description = "")
{
Code = (int)Enum.Parse(code.GetType(), code.ToString());
return this;
}
}
Yet what I'm doing when I call it is parsing in different Enums:
new CommonError().FromErrorCode((int)GeneralErrorCodes.SYSTEM_BASE_ERROR);
Cool. Here's the problem: I don't know of a way to do the inverse (which I need to do)
To do the reverse of this you need the Enum Type, eg:
Enum.GetNames(typeof(AnEmumType))
Enum.GetValues(typeof(AnEmumType)).ToList();

Related

Can't cast an `Enum` value to `int` in a generic method

If I have an enum...
public enum Frequency {
OneOff = 0,
Monthly = 1,
Quarterly = 2,
Annually = 3
}
...then I can do something like this...
int n = (int)Frequency.Annually;
Given that since C# 7.3, we are able to use Enum as a generic constraint, I expected to be able to do this...
public void Stuff<T>(T val) where T : Enum {
int v = (int)val;
}
...but the compiler complains, saying it can't convert type T to int.
Anyone able to explain this to me? The generic constraint tells the compiler that T is an enum, and (unless you specifically do it differently) an enum value can be cast to an int.
enum can be a long or other things
public static void Stuff<T>(T val) where T : System.Enum, IConvertible
{
int v = val.ToInt32(null);
}
This works
If you look at Enum you can see that its not stated as an int.
The actual base classes and implementations are:
public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
BTW: prefer long over int since longs are used as Flags in enumns to allow 64 flags

Get max value from enum using generics

How can I get the max int value from an enum using generics?
I have tried the following however it displays the following compilation error:
Cannot implicitly convert T to int
int maxValue = GetMaxValue<SomeEnum>(typeof(SomeEnum)); //expecting 2
private static int GetMaxValue<T>(Type enumType)
{
return Enum.GetValues(enumType).Cast<T>().Max();
}
public enum SomeEnum
{
ValueOne = 1,
Value = 2
}
In case of C# 7.3 or later version, you can implement Sweeper's idea in a bit different way (with a help of where T : Enum constraint):
public static T GetMaxValue<T>() where T : Enum {
return Enum.GetValues(typeof(T)).Cast<T>().Max();
}
...
SomeEnum max = GetMaxValue<SomeEnum>();
Please, note, that the result is enum itself and that's why we don't have any problems with enum underlying type (byte, short int, long)
You need to cast to int, not T. And you don't actually need the Type parameter (unless you don't know the type at compile time), since you can just do typeof(T):
private static int GetMaxValue<T>()
{
return Enum.GetValues(typeof(T)).Cast<int>().Max();
}
// usage:
GetMaxValue<SomeEnum>() // 2
If your enums have long or some other type as the underlying type, you can specify another type parameter to cast them to:
private static U GetMaxValue<T, U>() where U : struct
{
return Enum.GetValues(typeof(T)).Cast<U>().Max();
}
// usage:
GetMaxValue<SomeLongEnum, long>()
Use Convert.ToInt32
return Convert.ToInt32(Enum.GetValues(enumType).Cast<T>().Max());
But usually I do this, more faster than calling GetValues
public enum SomeEnum
{
ValueOne = 1,
Value = 2,
END
}
return (int)(SomeEnum.END - 1);

Casting T from and to Generic<T> (C#)

So I have a generic class, with Type as its generic parameter. In this class is a method, which has object parameter called value. Kinda like this:
public class Foo<Type> where Type : IComparable
{
public void Bar(object value)
{
DoSomething((Type)value);
}
}
As you can notice, I need to "DoSomething" with value (stored in object) that I first need to cast to Type. I even have my own overridden cast, which works on its own.
In this specific case, Type is generic, lets call it GenericType, and has this user-defined cast:
public static implicit operator GenericType<T>(T value)
{
return new GenericType<T>(value);
}
and value is an enum, lets say
public enum Number: short
{
Zero = 0,
One = 1,
Two = 2
}
The 'DoSomething((Type)value)' in this case is where Type is GenericType and value is Number.Zero. For some reason, this causes the cast to throw InvalidCastException: Specified cast is not valid. When I try it directly, i mean like..
GenericType<Number> z = (GenericType<Number>)Number.Zero;
..it works (I know, there is not explicit cast even needed). But for some reason, it does not work in the complex example I stated above. Can anyone help me understand and potentially fix that?
Why not just let your class use the generic type?
public class Foo<T> where T : IComparable
{
public void Bar(T value)
{
DoSomething(value);
}
}
No casting needed... and please don't use reserved words to name stuff.

Why do we need to type cast an enum in C#

I have an enum like:
public enum Test:int
{
A=1,
B=2
}
So here I know my enum is an int type but if I want to do something like following:
int a = Test.A;
this doesn't work.
If I have a class like:
public class MyTest
{
public static int A =1;
}
I can say ,
int a = MyTest.A;
Here I don't need to cast A to int explicitly.
So here I know my enum is an int type
No, it's not. It has an underlying type of int, but it's a separate type. Heck, that's half the point of having enums in the first place - that you can keep the types separate.
When you want to convert between an enum value and its numeric equivalent, you cast - it's not that painful, and it keeps your code cleaner in terms of type safety. Basically it's one of those things where the rarity of it being the right thing to do makes it appropriate to make it explicit.
EDIT: One oddity that you should be aware of is that there is an implicit conversion from the constant value 0 to the enum type:
Test foo = 0;
In fact, in the MS implementation, it can be any kind of constant 0:
Test surprise = 0.0;
That's a bug, but one which it's too late to fix :)
I believe the rest for this implicit conversion was to make it simpler to check whether any bits are set in a flags enum, and other comparisons which would use "the 0 value". Personally I'm not a fan of that decision, but it's worth at least being aware of it.
"The underlying type specifies how much storage is allocated for each enumerator. However, an explicit cast is needed to convert from enum type to an integral type".
With your updated example:
public class MyTest
{
public static int A =1;
}
And usage:
int a = MyTest.A;
That's not how enums look. Enums look more like (comments are places where we differ from a real enum):
public struct MyTest /* Of course, this isn't correct, because we'll inherit from System.ValueType. An enum should inherit from System.Enum */
{
private int _value; /* Should be marked to be treated specially */
private MyTest(int value) /* Doesn't need to exist, since there's some CLR fiddling */
{
_value = value;
}
public static explicit operator int(MyTest value) /* CLR provides conversions automatically */
{
return value._value;
}
public static explicit operator MyTest(int value) /* CLR provides conversions automatically */
{
return new MyTest(value);
}
public static readonly MyTest A = new MyTest(1); /* Should be const, not readonly, but we can't do a const of a custom type in C#. Also, is magically implicitly converted without calling a constructor */
public static readonly MyTest B = new MyTest(2); /* Ditto */
}
Yes, you can easily get to the "underlying" int value, but the values of A and B are still strongly typed as being of type MyTest. This makes sure you don't accidentally use them in places where they're not appropriate.
The enum values are not of int type. int is the base type of the enum. The enums are technically ints but logically (from the perspective of the C# language) not. int (System.Int32) is the base type of all enums by default, if you don't explicitly specify another one.
You enum is of type Test. It is not int just because your enum has integers values.
You can cast your enum to get the int value:
int a = (int) Test.A;

Adding constructor (or function) to enum

I am not sure if a constructor is exactly what I am looking for but if I explain what I am trying to do hopefully someone can tell me if I am trying to do is a silly idea or whether there are ways to do it.
So I have an enum:
public enum MessageType
{
Normal,
Error,
Chat,
Groupchat,
Headline
}
This enum is basically a wrapper for the jabber.net MessageType. So I want to create my enum from this. So at the moment I have a function like this:
private MessageType ConvertMessageType(JabberMessageType jabberType)
{
MessageType type = MessageType.Error;
switch (jabberType)
{
case JabberMessageType.normal:
type = MessageType.Normal;
break;
//etc
}
return type;
}
So I have to use enum MessageType type = ConvertMessageType(JabberMessageType.groupchat);
What I would like though is to be able to do something like:
enum MessageType type = MessageType(JabberMessageType.groupchat);
// or
enum MessageType type = MessageType.FromJabberJid(JabberMessageType.groupchat);
So that the conversion belongs with the enum rather than being a method outtside of.
Why not create an extension method to do this for you?
public static class ExtensionMethods
{
public static MessageType ConvertMessageType(this JabberMessageType jabberType)
{
switch(jabberType)
{
case JabberMessageType.normal:
return MessageType.Normal;
// Add rest of types here.
default:
return MessageType.Error;
}
}
}
Example usage:
var type = JabberMessageType.normal; // JabberMessageType
var messageType = type.ConvertMessageType(); // Converted to your custom MessageType
You can't do this. An Enumeration Type translates to an int (or byte, sbyte, short, uint, long or ulong if specified) value and is not technically a class.
An enumeration type (also named an enumeration or an enum) provides an
efficient way to define a set of named integral constants that may be
assigned to a variable.
One solution would be to put a static method to do this in a Utilities class.
MessageType type = Utilities.MessageTypeFromJabberJid(JabberMessageType.groupchat);
How about a class where your enum is nested?
Something like this
public static class Message
{
public enum Types
{
}
public static Message.Types ConvertMessageType(Message.Types jabberType)
{
}
}
You can write a simple one-liner which require less tweaking in future (in case further enum values are added), but this assumes, the enum names are the same in both types:
private MessageType ToMessageType(this JabberMessageType jabberType)
{
return Enum.Parse(typeof(MessageType), jabberType.ToString(), true);
}
This is some kind of a mapping functionality, from one type to another.

Categories