2013-03-25 111 views
3

我想創建一個通用的方法:通用類型約束禁止某些類型?

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, IEnumerable<T> value) 

我在我的代碼中的許多變量,是List<T>IEnumerable<T>xxxCollectionT[],等等。這個方法也有一個重載會採取非枚舉值。是否可以禁止類型參數約束中的特定類(如string)?

我已經創建了一個超負荷這樣的:

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, object value) 

此重載是良好的的單獨的值處理,但是處理值集合需要一個稍微不同的實現。但是,string實現IEnumerable,所以我的所有字符串變量都會被髮送到錯誤的重載,除非我能告訴編譯器它們應該被排除。

回答

2

考慮到您打算處理string,您無法對此使用約束條件並且不需要它們。

鑑於現有的方法,該方法接受類型objectstring類型的參數會導致你提出的新的重載,因爲IEnumerable<char>object更好的匹配。但是,您不必放棄重載的想法,也不需要求助於運行時檢查。只需爲string創建一個額外的過載。通過使編譯器的場景變得更容易,完成了objectIEnumerable<T>之間的整個決定。

鑑於

void Foo(object argument) 
{ 
    Console.WriteLine("object"); 
} 

void Foo<T>(IEnumerable<T> argument) 
{ 
    Console.WriteLine("enumerable T"); 
} 

void Foo(string argument) 
{ 
    Console.WriteLine("string"); 
} 

Foo("hello"); 
Foo(1); 
Foo(new int[] { 1 }); 

的方法調用列表生成輸出

string 
object 
enumerable T 

然後,您可能會進一步迫使你的字符串轉換成你的對象超載在一個地方。

void Foo(string argument) 
{ 
    // Console.WriteLine("string"); 
    Foo((object)argument); 
} 
+0

是我要去的路線。在我可以驗證我的結果之前(我正在重構整個視圖,去除與HTML混合的可能出錯的邏輯。)http://stackoverflow.com/questions/15577463/how-to-tell-ie - 忽略之前收到的格式不正確的html和渲染 - 不同) – Zarepheth 2013-03-25 18:39:46

+0

+1。如果你想「排除」(作爲勸阻/防止),你可以將'string'超載標記爲過時。 – 2013-03-25 18:40:18

+0

我只是想讓編譯器在遇到字符串而不是IEnumerable重載時使用'object'重載,所以我不會將任何標記標記爲過時。 – Zarepheth 2013-03-25 18:41:58

2

假設您只想將此擴展名用於模型類型,您可以創建一個Model基類並將T限制爲實現它的類型。

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, IEnumerable<T> value) 
    where T : ModelBase 
+0

我打算在模型的幾個屬性中的每一個上使用它。大多數屬性都是單個值,通常是'string'。一些是收藏品,通常是'List ',一些是其他類型的。由於從視圖看,該方法的意圖對於每個屬性都是相同的,因此我計劃使用重載來實現個別值和值集合之間稍微不同的呈現。 – Zarepheth 2013-03-25 18:10:12

3

Generic type parameters約束只能執行正約束約束。

運行時類型檢查可能是要走的路:

MvcHtmlString MyMethod<T>(this HtmlHelper html, string title, IEnumerable<T> value) 
{ 
    if (typeof(T) == typeof(string)) 
    { 
     throw new IllegalOperationException(...); 
    } 
} 

如果你想傳遞給你的方法類型從同一個基類繼承,或實現相同的接口,你可以使用jrummell對強制繼承的單個約束的建議。

雖然它的更不雅的,這樣做的另一種方式,如果你想支持異構類型會提供足夠的過載到處理您的特定用例:

// supports int, float, DateTime, etc. 
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<int> value) 
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<float> value) 
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<DateTime> value) 

// supports implementations of MyInterface 
MvcHtmlString MyMethod(this HtmlHelper html, string title, IEnumerable<IMyInterface> value) 

在上面的例子中,你將無法致電MyMethod<string>,因爲它不符合任何提供的類型約束。請注意,您不能使用類型約束,因爲它們不是方法簽名的一部分。

+2

如果在泛型類「Foo 」中將'MyMethod'設置爲非泛型方法,則可以將類型檢查放在靜態構造函數中,以便每次運行應用程序時只需檢查一次約束,而不是每次運行一次時間'MyMethod'被調用。 – 2013-03-25 17:58:45

+0

@ 280Z28我從來沒有想到這一點,但這是一個很好的觀點。 – 2013-03-25 18:02:18

+0

確保使用非常特定的/詳細的異常消息,因爲跟蹤在靜態構造函數中拋出的異常可能會遇到其他挑戰。 – 2013-03-25 18:05:18

1

不幸的是,沒有。

只能指定以下類型的約束:

where T : struct    // T must be a value type 
where T : class    // T must be a reference type 
where T : new()    // T must have a parameterless constructor 
where T : <base class name> // T must inherit from <base class> 
where T : <interface name>  // T must implement <interface> 
where T : U     // T must inherit from U, where U is another 
           // generic parameter 

參考:Constraints on Type Parameters

現在,您可以使用其中的一些限制,但不能將類型鎖定,只能指定允許的類型,例如接口實現約束。

您可以使用接口或基類約束來具體說「我允許的類型都必須具有以下特徵」。我會說這將是你最好的選擇。

你確定仿製藥在這裏是正確的工具嗎?

+0

我想要的是重載方法,以便列表以單一方式處理,而單獨的值以稍微不同的方式處理。我的問題的目的是讓編譯器將字符串放在單個值重載中,並在IEnumberable重載中列出。 從我的研究和到目前爲止的回覆中,我認爲我應該跳過重載併爲這兩種方法使用不同的名稱。 – Zarepheth 2013-03-25 18:02:21