2008-10-08 77 views
2

我已經有一個通用型的「G」編譯器無法轉換約束泛型類型

在我的類模型I類有

public class DetailElement : ElementDefinition 

比方說,我有這樣的

的方法
 public void DoSomething<G>(G generic) 
      where G : ElementDefinition 
     { 
      if (generic is DetailElement) 
      { 
       ((DetailElement)generic).DescEN = "Hello people"; //line 1 
       ////// 
       ElementDefinition element = generic; 
       ((DetailElement)element).DescEN = "Hello again"; //line 3 
       ////// 
       (generic as DetailElement).DescEN = "Howdy"; //line 5 
      } 
      else 
      { 
       //do other stuff 
      } 
     } 

編譯器報告一個錯誤在第1行:

Cannot convert type 'G' to 'DetailElement' 

但第3行工作正常。 我可以通過做寫一行代碼解決此問題5.

什麼,我想知道的是爲什麼編譯器報告在第1行,而不是一個在3號線的錯誤,因爲,據據我所知,它們是相同的。

編輯:恐怕我可能會丟失框架邏輯

EDIT2的一些重要的一塊:雖然編譯器錯誤的解決方案是很重要的,我的問題是關於爲什麼編譯器第1行,並報告錯誤不在第3行。

回答

7

如果G被限制爲一DetailElementwhere G : DetailElement),那麼你可以繼續投G到ElementDefinition,即「(ElementDefinition) generic」。但是因爲G可能是運行時DetailElement以外的另一個ElementDefinition的子類,所以它不會在編譯時允許它在類型未知和不可驗證的情況下運行。

在3線從投類型爲已知的ElementDefinition因此,所有你正在做的是上投。編譯器不知道它在運行時是否會成功,但它會在那裏信任你。編譯器不那麼信任泛型。

as第5行中的運算符也可能返回null,並且編譯器不會靜態檢查類型以查看它是否安全。您可以使用as任意類型,而不僅僅是與ElementDefinition兼容的那些類型。

Can I Cast to and from Generic Type Parameters? MSDN上:

編譯器將只讓你隱式轉換泛型類型參數爲對象,或約束指定類型。

這樣的隱式轉換當然是類型安全的,因爲在編譯時發現任何不兼容性。

編譯器將讓您顯式轉換泛型類型參數的任何接口,而不是一類:

interface ISomeInterface {...} 
    class SomeClass {...} 
    class MyClass<T> 
    { 
     void SomeMethod(T t) 
     { 
     ISomeInterface obj1 = (ISomeInterface)t;//Compiles 
     SomeClass  obj2 = (SomeClass)t;  //Does not compile 
     } 
    } 

但是,您可以使用臨時強制從泛型類型參數強制轉換爲任何其他類型對象變量

void SomeMethod<T>(T t) 
    { object temp = t; 
    MyOtherClass obj = (MyOtherClass)temp; 
    } 

不用說,這種顯式轉換是危險的,因爲它可能會引發在運行時異常,如果用來代替一般類型參數的具體類型不從您明確強制轉換爲類型派生。

更好的方法是使用isas運算符代替冒着拋出異常的風險。如果泛型類型參數爲查詢類型,則is運算符返回true,如果類型兼容,則as將執行強制轉換,否則將返回null。

public void SomeMethod(T t) 
{ 
    if(t is int) {...} 

    string str = t as string; 
    if(str != null) {...} 
} 
1

不應該在where子句中「where G:DetailElement」?

在您編寫的代碼中,DetailElement是ElementDefinition,但ElementDefinition不一定是DetailElement。所以隱式轉換是非法的。

您可能會傳入此方法的其他類型的ElementDefinition?如果是這樣,當您嘗試將它們轉換爲DetailElement實例時,它們會拋出異常。

編輯:

好了,現在你已經改變了你的代碼,我可以看到你正在檢查的類型,以確保它確實是進入代碼塊之前DetailElement。不幸的是,事實是,即使你已經自己檢查過類型,你也不能隱式地downcast。我覺得你真的應該在你塊的開頭使用「爲」關鍵詞:

DetailElement detail = generic as DetailElement; 
if (detail == null) { 
    // process other types of ElementDefinition 
} else { 
    // process DetailElement objects 
} 

更好的是,爲什麼不使用多態性允許各種ElementDefinition來定義自己的DoSomething的方法,並讓CLR照顧你的類型檢查和方法調用?

+0

爲了簡單起見,我沒有把這裏的所有代碼。我會編輯它是正確的 – 2008-10-08 17:46:17

0

這將導致更多的代碼,如果你有很多ElementDefinitions你擔心的,但可能你會得到不涉及靈巧然後無稽之談。

public void DoSomething<G>(G generic) 
     where G : ElementDefinition 
    { 
     DetailElement detail = generic as DetailElement; 
     if (detail != null) 
     { 
      detail.DescEN = "Hello people"; 
     } 
     else 
     { 
      //do other stuff 
     } 
    } 

另一個可能的解決方案,當我需要這樣的信息時,我用了一個臨時對象變量。

DetailElement detail = (DetailElement)(object)generic; 

它的作品,但作爲形式可能是最好的。

1

通常情況下,上傳是一種代碼異味。您可以通過方法重載來避免它。試試這個:

public void DoSomething(DetailElement detailElement) 
{ 
    // do DetailElement specific stuff 
} 

public void DoSomething<G>(G elementDefinition) 
    where G : ElementDefinition 
{ 
    // do generic ElementDefinition stuff 
} 

然後,您可以通過使用此代碼利用方法重載的:

DetailElement foo = new DetailElement(); 

DoSomething(foo); // calls the non-generic method 
DoSomething((ElementDefinition) foo); // calls the generic method