I come across a bug in a method we have in our projects at work. The method takes a generic type and then it checks if the type is enum before it applies further logic to it. If the type is not an enum, it will throw an ArgumentException. The method is similar to the sample method below.
public static void DoSomething<T>(T value)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("Value must be an enum type");
// More logic go here
}
This method works fine for normal enum types. However, if the type T is a nullable enum, the ArgumentException is thrown.
It turns out that Type.IsEnum returns false for nullable enum even though the underlying type is enum. So in order to fix it, the method will also need to check if the underlying type is enum in case of nullable.
public static void DoSomething<T>(T value)
{
if (!typeof(T).GetNestedTypeOrDefault().IsEnum)
throw new ArgumentException("Value must be an enum type");
// More logic go here
}
There is another way to do it. I don’t recommend it since it is longer but it’s good to know anyway. For this second solution, I wrote the extension method below to perform this check.
public static class TypeExtensions
{
public static bool IsEnum(this Type type) =>
type.IsEnum || (Nullable.GetUnderlyingType(type)?.IsEnum ?? false);
}
Then, I fix the method to use this extension method to check if it is an enum instead of the default IsEnum property.
public static void DoSomething<T>(T value)
{
if (!typeof(T).IsEnum())
// No longer using the default IsEnum property
throw new ArgumentException("Value must be an enum type");
// More logic go here
}
EDIT: A person on Reddit pointed out to me that I can use the where keyword to enforce enum constraint. This will give compile time error which is the better option for generics. However, if you only have access to Type object, then the above solutions apply.
public static void DoSomething<T>(T value)
where T : Enum
{
// More logic go here
}