2010-10-16 91 views
4

编辑:我提交的微软的bug报告连接:: https://connect.microsoft.com/VisualStudio/feedback/details/614234/delegate-createdelegate-allows-binding-functions-with-enum-parameters-to-the-enum-base-type#details为什么Delegate.CreateDelegate允许无效转换?

考虑下面的事情:

public static Int32 Test(Int16 @short) 
{ 
    return @short; 
} 

和调用代码::

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. 

为什么能Delegate.CreateDelegate做这个奇怪的转换,没有其他方式创建一个功能允许?更糟糕的是,一个比较合理的转换说Int64Object都失败了。我意识到StringComparison“延伸”Int32,但我认为这是更多的编译器技巧,因为枚举的扩展Enum类。

此外,此转换也适用于DynamicMethod.CreateDelegate

编辑刚试过Nullable<Int32>它不起作用,这是令人困惑的。我认为唯一的“免费”转换是将Enum类型转换为其基础类型,但为什么?

请注意,这不允许您将int实例方法(如GetHashCode)转换为采用枚举类型的开放方法,这就是为什么我认为这是一个错误,因为行为不一致。

编辑: 如果我们删除test2和test3行,然后我们可以测试看到方法,委托和“非法”委托都按预期工作。

Console.WriteLine(Test(0)); // prints 0 
Console.WriteLine(test(0)); // prints 0 
Console.WriteLine(test4(0)); //prints CurrentCulture 

编辑: 下面是这一点,我在大约10分钟写了一个非常大的弊端。这为TEnum创建了一个IEqualityComparer<T>,通过基本上抓取它的基础类型,然后包装Equals和HashCode,并使用这个技巧/滥用将参数转换为TEnums而不是基础类型。如果这是我想知道的错误,那么我不会试图依赖这种行为。

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() 
    { 
    } 
} 
+0

也许下面的链接将很有用:http://stackoverflow.com/questions/2490828/createdelegate-with-unknown-types,http://kohari.org/2009/03/06/fast-late-bound- invocation-with-expression-trees /,http://www.west-wind.com/Weblog/posts/653034.aspx,http://social.msdn.microsoft.com/Forums/en-US/roboticsccr/thread/2e80a245-607e-4f15-93ad-1f74ae5406f1 – 2010-10-16 17:08:02

+0

这里很好Jon Skeet的帖子:http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx – 2010-10-16 17:15:25

+0

当你使用这种方法时,你的任何关于Atum和Int之间的隐式转换都没有提及。他们也都在谈论使用LGC编写代码,这种方法正在避免。 – 2010-10-16 17:21:18

回答

2

这不是一个错误。总是可以将整型值类型转换为像StringComparison这样的枚举类型。编译器通常需要强制转换,但您在此绕过编译器。就像在C#中一样,没有检查来验证整数值实际上代表枚举值之一。

+0

你确定吗?当我检查'test4.Method'时,它说它的“Int32 Test(Int16)”,这是没有道理的,因为它是完全错误的。我写了很多带表达式的代码,并且我必须总是为返回或'Expression '输入'Int32-> enum'类型.Compile()'会抛出一个异常,指出无效的返回类型。 – 2010-10-16 17:24:51

+0

它很有意义,这是* test *,'Func '的委托定义。Expression类有其自己的管道来检查转换,比CLR允许的限制更多。 – 2010-10-16 18:16:26