2011-09-22 64 views
4

我四處搜尋,但沒有找到做任何的例子,雖然這是有幫助的:通用T搭配枚舉和鑄造噸至ENUM

Create Generic method constraining T to an Enum

我有在包裝功能的通用功能API(我無法觸及)。包裝函數需要一個System.Enum並返回相同的值。我的通用版本在這個例子的非剝離版本中簡化了很多事情。

問題是,我不能從T到System.Enum,或者返回,因爲T不限於System.Enum(至少這是我的理解)。

下面的代碼工作,但我很好奇,想知道是否有任何隱藏的陷阱,或者更好的辦法,因爲我是很新的仿製藥:

using System 
using System.Collections.Generic 
... 

    public T EnumWrapper<T>(T enumVar) where T : struct, IFormattable, IConvertible, IComparable 
    { 
     if (!typeof(T).IsEnum) 
      throw new ArgumentException("Generic Type must be a System.Enum") 

     // Use string parsing to get to an Enum and back out again 
     Enum e = (Enum)Enum.Parse(typeof(T), enumVar.ToString()); 
     e = WrappedFunction(e); 
     return (T)Enum.Parse(typeof(T), e.ToString()); 
    } 

如果這是確定的,然後讓這個作爲一個例子。我找不到這個,至少這是一個有效的解決方法。

P.S.在這種情況下,性能不是問題。我以爲我在想字符串工作可能會很慢,我總是對性能技巧感興趣。

+0

新增IFormattable和IComparable(謝謝@Michael B): http://stackoverflow.com/questions/7508455/generic-t-with-enum-and-casting-t-to-enum/7516220#7516220 – Rafe

回答

4

您可以使約束更緊。所有枚舉實現可遵循的接口IFormattableIConvertibleIComparable。這幾乎將你限制在像他們一樣行事的基元和枚舉類中。

或者,如果你真的想創建一個方法或類只可綁定到枚舉,你可以做到以下幾點:

public abstract class InvokerHelper<T> where T : class 
    { 
     public abstract R Invoke<R>(R value) where R : struct,T; 
    } 


     public class EnumInvoker : InvokerHelper<Enum> 
     { 
      public override R Invoke<R>(R value) 
      { 
       return (R)WrappedMethod(value); 
      } 
     } 

其笨重,不能用於擴展方法,但你能做出這樣一個單例對象,然後只有通用方法。

或者,你可以寫在C++/CLI您的代碼或Reflection.Emit它允許你創建可以有where T:struct,Enum

其實看來你只是想利用一個通用的牛逼調用了一個枚舉的方法的限制類和返回它作爲T對嗎?

然後下面的工作。

public static T WrappedMethodInvoker<T>(T value) where T:struct,IComparable,IFormattable,IConvertible 
{ 
    Enum e; 
    if((e = value as Enum) == null) 
     throw new ArgumentException("value must be an Enum") 
    object res = WrappedMethod(e); 
    return (T)res; 
} 

這樣會容易一些。首先我們知道T是Enum,您可以使用as運算符來嘗試轉換爲可空類型(引用類型或可空結構),其中Enum肯定是。從那裏我們可以使用返回類型的協方差,WrappedMethod返回Enum,這意味着我們可以將它存儲在一個對象中。最後,當你在泛型方法中有一個對象時,你在語法上允許將它轉換爲T.它可能會失敗,但我們知道該方法返回一個相同類型的Enum。

有一些費用知道,你總是拳擊和拆箱。包裝的方法是通用的嗎?

我已經編輯了答案,以顯示修改爲您的示例的第一種技術。它非常簡單和類型安全,但你總是必須有一個EnumInvoker來執行這些操作。 不幸的是,兩個答案都應該執行相同的操作,因爲如果參數爲Enum,則調用WrappedMethod將框值。第一種方法的唯一優點是它是強類型的,但通用虛擬方法是我所知道的最慢的方法。因此避免使用類型檢查可能不值得他花費更簡單的調用。

+0

@ Michawl B:謝謝你的迴應。你可以請教擴展這個教育目的嗎?對於C#我還是有點綠色(我的背景更多的是CG腳本,例如Python,JavaScript等)。您可以發佈此解決方案的使用示例嗎?這可以用於我必須通過並接收System.Enum(來自/從被包裝的函數中)並且傳遞的參數是一個枚舉(再次因爲我在API中工作 - Unity遊戲引擎)的情況。 – Rafe

+0

我添加了一個更簡單的方法來完成所需的工作。 –

+0

我使用了最後一個建議,效果很好。感覺比絃樂手更舒服。你的唯一錯字是在投球后丟失分號。謝謝!! – Rafe

2

問題很簡單:C#的類型系統不允許您指定Enum類型的約束。該代碼沒有明顯的缺陷,除非它傳遞了一個不是Enum的IConvertible結構,否則不會觸發編譯時錯誤。在這種情況下,它會在運行時失敗,這是不可取的,但沒有更好的選擇,真的。

雖然我不太瞭解CodeContracts(雖然我愛上了它們),但在編譯時您可以使用CodeContracts強制執行此操作,以便在此處給出明確的答案。