Edit: I filed a bug report on microsoft connect:: https://connect.microsoft.com/VisualStudio/feedback/details/614234/delegate-createdelegate-allows-binding-functions-with-enum-parameters-to-the-enum-base-type#details
Consider the following thing:
public static Int32 Test(Int16 @short)
{
return @short;
}
and from calling code::
Func<Int16,Int32> test = Test; //valid method group conversion.
Func<Int16,StringComparison> test2 = Test; // doesn't compile, not valid method group conversion.
Func<Int16,StringComparison> test3 = test; // doesn't compile, invalid contra-variance.
Func<Int16,StringComparison> test4 = Delegate.CreateDelegate(typeof(Func<Int16,StringComparison>),test.Method) as Func<Int16,StringComparison>; // works fine.
Why can Delegate.CreateDelegate do this strange conversion that no other means of creating a function allows? Even worse, a somewhat more sensible conversion to say Int64
or Object
both fail. I realize that StringComparison
"extends" Int32
, But I thought that was more of a compiler trick as enum's extend the Enum
class.
Also, this conversion works for DynamicMethod.CreateDelegate
, as well.
Edit just tried it with Nullable<Int32>
it doesn't work, which is perplexing. I think the only "free" conversion is to Enum
types to their underlying type, but why?
Note that this does not allow you to convert an int instance method (like GetHashCode) to an open method that takes the enum type, which is why I think this is a bug as the behavior is inconsistent.
Edit: If we remove test2 and test3 line we can then test to see that the method,delegate, and the "illegal" delegate all work as expected.
Console.WriteLine(Test(0)); // prints 0
Console.WriteLine(test(0)); // prints 0
Console.WriteLine(test4(0)); //prints CurrentCulture
Edit:
Here is a very big abuse of this that I wrote in about 10 minutes. This creates an IEqualityComparer<T>
for an TEnum
, by basically grabbing the one for its an underlying type and then just wrapping the Equals, and HashCode and using this trick/abuse to convert the parameters to TEnums, rather than underlying type. If this is a bug I'd like to know so that I won't try to rely on this behavior.
class EnumComparer<TEnum> : EqualityComparer<TEnum> where TEnum : struct
{
static Func<TEnum, TEnum, bool> s_Equals;
static Func<TEnum, int> s_HashCode;
static EnumComparer<TEnum> s_default;
static EnumComparer()
{
if (!typeof(TEnum).IsEnum) throw new Exception("Not an enum type");
Type underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
object equalityComparer = typeof(EqualityComparer<>).MakeGenericType(new[] { underlyingType }).GetProperty("Default").GetGetMethod().Invoke(null, null);
s_Equals = Delegate.CreateDelegate(typeof(Func<TEnum, TEnum, bool>), equalityComparer,equalityComparer.GetType().GetMethod("Equals", new[]{underlyingType,underlyingType})) as Func<TEnum,TEnum,bool>;
s_HashCode = Delegate.CreateDelegate(typeof(Func<TEnum, int>), equalityComparer, equalityComparer.GetType().GetMethod("GetHashCode", new[]{underlyingType})) as Func<TEnum, int>;
s_default = new EnumComparer<TEnum>();
}
public static new EnumComparer<TEnum> Default
{
get
{
return s_default;
}
}
public override bool Equals(TEnum x, TEnum y)
{
return s_Equals(x, y);
}
public override int GetHashCode(TEnum obj)
{
return s_HashCode(obj);
}
private EnumComparer()
{
}
}