2013-03-11 32 views
6

所以我有一個接受泛型類型參數的類,如果類型參數是給定類型的子類,則會進行一些特殊處理。在LINQ查詢中鑄造泛型類型

IEnumerable<T> models = ... 

// Special handling of MySpecialModel 
if (filterString != null && typeof(MySpecialModel).IsAssignableFrom(typeof(T))) 
{ 
    var filters = filterString.Split(...); 
    models = 
     from m in models.Cast<MySpecialModel>() 
     where (from t in m.Tags 
       from f in filters 
       where t.IndexOf(f, StringComparison.CurrentCultureIgnoreCase) >= 0 
       select t) 
       .Any() 
     select (T)m; 
} 

但如果我改變使用as而不是鑄造的代碼我得到的最後一行

Cannot convert type 'MySpecialModel' to 'T' 

一個例外,我得到這個錯誤。

The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint. 

我在這裏錯過了什麼?

更新

此類需求可以採取任何類型的參數,包括struct S和內置的類型,所以通用約束就不會在我的情況下,合適的解決方案。

+0

你是否在泛型類中設置了約束'where T:class'? – 2013-03-11 21:53:58

+0

@danradu不,但這不適用於我的情況,因爲泛型類可以接受參考和值類型參數。 – 2013-03-11 21:57:32

+0

@ p.s.w.g,參見更新 – smartcaveman 2013-03-11 21:57:54

回答

3

執行 Select(x => (MySpecialModel)x)

的LINQ Cast<T>方法只適用於鑄造元件工作,該元件已經是(如一個基本類型的派生類型,或接口)。它無意投射能夠投射到目標類型的物體。 (如 new List<int>{1,2,3}.Cast<long>()會拋出異常也是如此。

以上答案是沒有錯,但它並沒有解決這個問題。

僅僅因爲你已經證明與反思,一個泛型參數必然一個給定的類型,並不意味着編譯器知道它是爲了做到這一點,你需要將你的T實例轉換爲一個通用類型(例如object),然後將其轉換爲特定類型,例如(將您的查詢中的最後一行更改爲select (T)(object)m應該有效。

+0

試過'Select(x =>(T)x)'。同樣的結果 – 2013-03-11 21:51:46

+0

ohh我的壞,改變你的最後一行'select(T)(object)m') - 我在回答 – smartcaveman 2013-03-11 21:53:56

+0

之前沒有完全讀完這個問題。謝謝。雙重作品,但看起來很奇怪。我最終以'.Cast ()'(這是等同的,更美觀)結束了。如果你知道任何理由,雙投會更好,請讓我知道。 – 2013-03-11 22:10:42

1

要使用as關鍵字,請輸入在您的泛型參數class約束:

void MyMethod<T>(T item) where T : class 
{ 
    //... 
} 
+0

我不想這樣做,因爲這個類也可以用於'struct'。 – 2013-03-11 21:53:22

1

如果您知道泛型類型將永遠是一個類,你可以在你的類添加類型約束:

public class Test<T> where T : class {} 

否則執行雙投通過對象smartcaveman曾建議:

.Select(x => (T)(object)x); 
+0

再次閱讀 - 我第一次瀏覽了,但他實際上是在詢問如何將參數'T'直接投射到'MySpecialModel',這是不可能的。 – smartcaveman 2013-03-11 22:00:28

0

您可能適用Nullable<T>約束 - 應該能夠投射(至少使用「as」)。

1

最簡單的解決方法是先投給T前投給object

select (T)(object)m; 

的問題是你的支票在運行時發生,但是編譯器不知道T必須是一個實例MySpecialModelif聲明中。因此,它只是看到你正在試圖從MySpecialModel投射到一些任意類型的T,這是不安全的,因此是錯誤的。

2

請嘗試以下

select (T)(object)m; 

在運行時您確認TMySpecialModel一個亞型,但是編譯器不能夠訪問在編譯時該信息。它只看到了兩種不相關類型之間的嘗試轉換:TMySpecialModel

要解決此問題,您需要使用object作爲中間人。編譯器理解如何將MySpecialModel轉換爲object並將其從object轉換爲T