2011-10-20 47 views
4

以下代碼給出了Main函數中第二行標題中的錯誤。運營商'??'不能應用於子類別的操作數

public class P {} 

public class B : P {} 

public class A : P {} 

void Main() 
{ 
    P p = GetA()??GetB(); 
} 

public A GetA() 
{ 
    return new A(); 
} 

public B GetB() 
{ 
    return new B(); 
} 

一個簡單的調整,以這樣的

p = (P)GetA()??GetB(); 
    or 
    p = GetA()??(P)GetB(); 

的作品就行了。

我很好奇爲什麼編譯器不明白,這兩個都是左側容器的子類,並允許沒有轉換的操作?

回答

6

左側參數的類型必須與右側類型兼容,反之亦然。換句話說,必須存在從BA或從AB的隱式轉換。

var a = x ?? y; 

在上文中,如果存在來自yx的隱式轉換然後x類型變爲表達式的類型。如果沒有從yx的隱式轉換,但存在從xy的隱式轉換,則y的類型將變爲表達式的類型。如果兩個方向都不存在轉換,繁榮;編譯錯誤。從規格:

表達式的類型a ?? b取決於操作數類型之間的哪些隱式轉換可用。按照偏好的順序, b是A0,A或B,其中A是a的類型,B是b的類型(假設b具有類型),並且A0是A的基礎類型(如果A是可空類型),否則A 。具體來說,一個?? b的處理如下:

•如果A不是可爲空類型或引用類型,則會發生編譯時錯誤。

•如果A是可以爲空的類型,並且存在從b到A0的隱式轉換,則結果類型爲A0。在運行時,首先評估a。如果a不爲空,則將a解包爲A0類型,並且成爲結果。否則,評估b並將其轉換爲A0類型,並將其作爲結果。

•否則,如果存在從b到A的隱式轉換,則結果類型爲A.在運行時,首先計算a。如果a不爲空,則a成爲結果。否則,評估b並將其轉換爲A類型,並將其作爲結果。

•否則,如果b具有類型B並且存在從A0到B的隱式轉換,則結果類型爲B.在運行時,首先評估a。如果a不爲空,則將a解包爲類型A0(除非A和A0是相同類型)並將其轉換爲類型B,並且這將成爲結果。否則,b被評估併成爲結果。

•否則,a和b不兼容,併發生編譯時錯誤。

+0

啊我明白了,我是在寫一個非常複雜的評論的過程中,當我重讀你的榜樣,並意識到LHS的類型源自對RHS表達的評估。最初,我認爲只要'x'和'y'都可以轉換爲'a',那麼它就很好! – Joe

+0

是的,包括被分配給變量的類型是最直接的方式,以及我最初以爲它會起作用。我很想聽到像Eric Lippert這樣的人爲什麼這樣設計。 –

-1

操作符的操作數?應該是同一類型:

P a = GetA(); 
    P b = GetB(); 
    P p = a ?? b; 
+1

它們不必是相同的類型,它們之間必須存在一個*隱式轉換*。 –

1

我很好奇,爲什麼編譯器不明白,都是左手側容器的子類,並允許操作,而投?

因爲那麼這將被允許:

public class SqlConnection : object {}  
public class Random : object {} 

public SqlConnection GetA() { return new SqlConnection(); } 
public Random GetB() { return new Random(); } 

void Main() 
{ 
    var p = GetA() ?? GetB(); 
} 
+0

+1我喜歡這個例子:) –

+0

哼哼......但是等一下! 'var p =(object)GetA()?? GetB();'是允許的。它可能在語法上有所不同,但達到相同的邏輯目的。 – Joe

+0

@Joe:您可以將任何東西投射到對象,並且由於存在從B到對象的隱式轉換,所得到的表達式將返回對象。在這裏使用var會傷害這個例子,因爲如果你執行轉換,p將是類型對象。 –

相關問題