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
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
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.
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.
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
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.