C# - Generic enum type casting - c#

I have a few questions about generic classes with Enums.
First of all, I declared my class like this:
public class MyClass<TEnum> where TEnum : struct, IConvertible
But, I'm getting an error that states that my class cannot be used with type arguments.
Moreover, I need to convert the Enum's value to an Integer. How can I do that?
public void SomeMethod(TEnum value)
{
int a = (int)value; // Doesn't work, need to cast to Enum first (?).
}
Thanks.

You already have what you need since you declared requirement IConvertible. Just use ToInt32 etc methods:
public class MyClass<TEnum> where TEnum: struct, IConvertible
{
public int SomeMethod(TEnum value)
{
return value.ToInt32(null);
}
}
For example .NET type decimal is a struct and an IConvertble:
MyClass<decimal> test = new MyClass<decimal>();
Console.WriteLine(test.SomeMethod(150m));
For other classes be sure that you implement IConvertible.

You have declared your generic type parameter to implement IConvertible and that interface has a ToInt32 method.

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

C# 7.3 Enum constraint: Why can't I use the nullable enum?

Now that we have enum constraint, why doesn't compiler allow me to write this code?
public static TResult? ToEnum<TResult>(this String value, TResult? defaultValue)
where TResult : Enum
{
return String.IsNullOrEmpty(value) ? defaultValue : (TResult?)Enum.Parse(typeof(TResult), value);
}
The compiler says:
Error CS0453 The type 'TResult' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'
You can, but you have to add another constraint: the struct constraint.
public static void DoSomething<T>(T? defaultValue) where T : struct, Enum
{
}
Because System.Enum is a class, you cannot declare a variable of type Nullable<Enum> (since Nullable<T> is only possible if T is a struct).
Thus:
Enum? bob = null;
won't compile, and neither will your code.
This is definitely strange (since Enum itself is a class, but a specific Enum that you define in your code is a struct) if you haven't run into it before, but it is clearly a class (not a struct) as per the docs and the source code.

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.

Cast needed for generic constraints

I have created a method with two generic parameters where one parameter (itemsToAdd) must be the same type as the generic parameter of the next parameter (inputList).
See this demo code:
public class GenericsDemo
{
public void AddToList<TList, TItems>(TList inputList, params TItems[] itemsToAdd)
where TItems : IConvertible
where TList : IEnumerable<TItems>
{
IEnumerable<IConvertible> someOtherList;
// Sounds good, doesn't work..
//someOtherList = inputList;
// This works
someOtherList = (IEnumerable<IConvertible>)inputList;
}
}
I would expect the inputList can be directly assigned into the IEnumerable<IConvertible> someOtherList, but it needs a cast. Why the cast is needed?
Covariance only works for classes, not for structs (Source).
Thus, if you restrict TItems to reference types, your code compiles (fiddle):
where TItems : class, IConvertible

Extension Methods for IEnumerable<Enum>?

I have a bunch of different enums, such as...
public enum MyEnum
{
[Description("Army of One")]
one,
[Description("Dynamic Duo")]
two,
[Description("Three Amigo's")]
three,
[Description("Fantastic Four")]
four,
[Description("The Jackson Five")]
five
}
I wrote an extension method for any Enum to get the Description attribute if it has one. Simple enough right...
public static string GetDescription(this Enum currentEnum)
{
var fi = currentEnum.GetType().GetField(currentEnum.ToString());
var da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute));
return da != null ? da.Description : currentEnum.ToString();
}
I can use this very simply and it works like a charm, returning the description or ToString() as expected.
Here is the problem though. I would like to have the ability to call this on an IEnumerable of MyEnum, YourEnum, or SomeoneElsesEnum. So I wrote the following extension just as simply.
public static IEnumerable<string> GetDescriptions(this IEnumerable<Enum> enumCollection)
{
return enumCollection.ToList().ConvertAll(a => a.GetDescription());
}
This doesn't work. It compiles fine as a method, but using it gives the following error:
Instance argument: cannot convert from 'System.Collections.Generic.IEnumerable<MyEnum>' to System.Collections.Generic.IEnumerable<System.Enum>'
So why is this?
Can I make this work?
The only answer I have found at this point is to write extension methods for generic T as follows:
public static IEnumerable<string> GetDescriptions<T>(this List<T> myEnumList) where T : struct, IConvertible
public static string GetDescription<T>(this T currentEnum) where T : struct, IConvertible
Someone must have a better answer for this, or an explanation of why I can extend an Enum but not an IEnumerable of Enum...
Anyone?
.NET generic covariance only works for reference types. Here, MyEnum is a value type, and System.Enum is a reference type (casting from an enum type to System.Enum is a boxing operation).
So, an IEnumerable<MyEnum> is not an IEnumerable<Enum>, as that would change the representation of each enumerated item from a value type to a reference type; only representation-preserving conversions are allowed. You need to use the generic method trick you've posted to get this to work.
Starting from v4 C# supports co-variance and contra-variance for generic interfaces and delegates. But unfortunately, these *-variances work only for reference types, it doesn't work for value types, such as enums.

Categories