2010-10-16 111 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

相關問題