2012-03-08 25 views
5

這是部分好奇心和部分原因,因爲我只是想用這個。如果您有以下定義,編譯器將不允許這樣做,因爲它表示該成員已經定義。不允許泛型類型參數的獨佔重載的原因是什麼?禁用重載泛型類型參數嗎?

void Get<T>() where T: struct {} 
void Get<T>() where T: class {} 

在我看來,這沒有固有的問題。有人可能會爭辯說,在定義重疊的情況下,編譯器應該選擇哪種方式並不總是很清楚(但通常的解決方案似乎是最具體匹配的)。

有人可以幫助我理解或指出一個資源背後的原因是什麼?

+0

出於興趣,您的實際用例試圖做什麼樣的事情? – AakashM 2012-03-08 15:51:06

+0

@AakashM:簡單,我需要以不可空的值類型以不同的方式轉換可空類型。我認爲一個通用的重載是解決它的一個快速方法。該方法需要一個對象和一個類型參數,我不能限制類型參數(嗯,我可以,但我不能超載它)。 – Abel 2012-03-08 19:16:01

回答

9

埃裏克利珀已經回答了這一個,在通用的約束和方法簽名博客文章:泛型類型http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

約束不是在CLR方法簽名的一部分,所以你不能有兩個方法僅在泛型類型約束上不同。如果沒有CLR支持,讓C#以一種與其他.NET語言兼容的明智方式支持這些方法將非常花哨。

+0

如果你這樣說,這似乎合乎邏輯。人們只能懷疑,爲什麼它不是方法簽名的一部分? Anders Hejlsberg有沒有其他方式設計它的好理由?還沒有完全閱讀Lippert的文章。感謝指針。 – Abel 2012-03-08 15:44:41

+1

我的猜測是這是一個CLR限制--CLR也不考慮通用類型約束方法簽名的一部分。沒有辦法在方法簽名的二進制表示中表示通用約束,所以它有相當深的侷限性。 – thecoop 2012-03-08 16:08:07

+4

@Abel:thecoop是正確的;這是CLR的限制。理論上我們當然可以將約束作爲簽名的一部分,這對您的具體情況肯定會有所幫助。但在一般情況下,它提出了很多問題。例如:'void M ()其中T:IFoo {}'和'void M ()其中T:IBar {}'將是不同的方法簽名;那麼'M '哪種方法'Blah'是一個同時實現IFoo和IBar的類?處理模糊簽名的規則已經足夠複雜了;讓我們不要添加一個全新的方法來模糊。 – 2012-03-08 16:25:32

1

Nullable<T>struct約束是恕我直言,真的很不幸。像Nullable<String>Nullable<Nullable<Nullable<int>>>可能會浪費,但那又如何?將前者作爲其內容;將其作爲內容取消裝箱,如果內容非空,則設置HasValue。將前者作爲int,如果所有可空白報告爲HasValue,並且拆箱時,如果內容非空,則設置所有嵌套項目的HasValue

否則,我建議您創建一個類型參數爲T的靜態泛型類,該靜態泛型類包含一個接受T作爲參數的委託屬性。該屬性應該返回應該初始化的私有字段的內容,以指向將檢查T類型的方法,並根據需要將代理設置爲structclass版本。

下面是我正在談論的一個示例;這個使用了各種接口約束而不是結構/類約束,但是同樣的原則可以被有效地使用。

 
     static class _FooDispatcher<T> 
     { 
      public static Action<T> Foo = setupFoo; 

      static void doFooWithIGoodFoo<TT>(TT param) where TT : IGoodFoo 
      { 
       Console.WriteLine("Dispatching as IGoodFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
       param.Foo(); 
      } 
      static void doFooWithIOkayFoo<TT>(TT param) where TT : IOkayFoo 
      { 
       Console.WriteLine("Dispatching as IOkayFoo with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
       param.Foo(); 
      } 
      static void doFooSomehow<TT>(TT param) 
      { 
       Console.WriteLine("Nothing exciting with {1} type {0}", typeof(TT).ToString(), typeof(TT).IsValueType ? "value" : "reference"); 
      } 
      static void setupFoo(T param) 
      { 
       System.Reflection.MethodInfo mi; 
       if (typeof(IGoodFoo).IsAssignableFrom(typeof(T))) 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIGoodFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       else if (typeof(IOkayFoo).IsAssignableFrom(typeof(T))) 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooWithIOkayFoo", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       else 
        mi = typeof(_FooDispatcher<T>).GetMethod("doFooSomehow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 
       Foo = (Action<T>)(@Delegate.CreateDelegate(typeof(Action<T>), mi.MakeGenericMethod(typeof(T)))); 
       Foo(param); 
      } 
     }