2016-11-14 27 views
4

這段代碼今天抓到我了:C#三元運算符評價時應不

clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value] 

clientFile.Review月是一個字節?在失敗的情況下,它的價值爲零。 預期的結果類型是字符串。

唯一的例外是在此代碼

public static implicit operator string(LookupCode<T> code) 
    { 
     if (code != null) return code.Description; 

     throw new InvalidOperationException(); 
    } 

評價的右手側被評估,然後隱式轉換爲字符串。

但是我的問題是,爲什麼只有左手側得到評估時纔會評估右手? (該文件指出,「只有兩個表達式的一個評價。」)

解決方案順帶是投空字符串 - 這工作,但ReSharper的告訴我說劇組是多餘的(我同意)

編輯:這與「爲什麼我需要在編譯之前添加一個類型」類型三元運算符問題不同。這裏的要點是,不需要強制轉換就可以編譯 - 只是爲了使其正常工作。

+1

ReSharper的是錯在這裏 –

+2

什麼是'MonthNames.AllValues'的類型?一個MCVE會有幫助 –

+2

可能的重複[有條件的操作符賦值可爲Nullable 類型?](http://stackoverflow.com/questions/75746/conditional-operator-assignment-with-nullablevalue-types) – raidensan

回答

3

你忘記隱式運算符是在編譯時確定的。這意味着你所擁有的null實際上是LookupCode<T>類型(由於類型推斷在三元運算符中工作的方式),並且需要使用隱式運算符將其轉換爲字符串;這就是你的例外。

void Main() 
{ 
    byte? reviewMonth = null; 

    string result = reviewMonth == null 
        ? null // Exception here, though it's not easy to tell 
        : new LookupCode<object> { Description = "Hi!" }; 

    result.Dump(); 
} 

class LookupCode<T> 
{ 
    public string Description { get; set; } 

    public static implicit operator string(LookupCode<T> code) 
    { 
     if (code != null) return code.Description; 

     throw new InvalidOperationException(); 
    } 
} 

無效操作不會對第三個操作數發生,它發生在第二個 - null(實際上是一個default(LookupCode<object>))是string型的沒有,所以隱式操作符被調用。隱式運算符引發無效操作異常。

你可以很容易地看到這一點,如果您使用的是稍微修改的代碼是正確的:

string result = reviewMonth == null 
       ? default(LookupCode<object>) 
       : "Does this get evaluated?".Dump(); 

你仍然可以得到一個無效的操作異常,第三個操作數未評估。這在生成的IL中當然是非常明顯的:兩個操作數是兩個獨立的分支;他們都無法執行。和第一支有另一個非常明顯的事情:

ldnull  
call  LookupCode`1.op_Implicit 

它甚至不是隱藏在任何地方:)

解決方法很簡單:使用顯式類型nulldefault(string)。 R#完全是錯誤的 - (string)nullnull不一樣,在這種情況下R#有錯誤的類型推斷。

當然,這是在C#規範(14.13 - 有條件的操作者)的所有描述:

所述的第二和第三個操作數:操作員控制的條件表達式的類型?。

設X和Y爲 爲第二個和第三個操作數的類型。然後,

  • 如果X和Y是相同的類型,那麼這是條件表達式的類型。否則,如果存在從X到Y而不是從Y到X的隱式轉換(第13.1節),則Y是條件表達式的類型 。否則,如果存在從Y到X而不是從X到Y的隱式轉換(第13.1節),則X是條件表達式的類型 。
  • 否則,不能確定表達式類型,併發生編譯時錯誤。

在你的情況下,隱式轉換存在從LookupCode<T>string,而不是相反的,所以類型LookupCode<T>優選string。有趣的一點是,因爲這是所有在編譯時完成的,轉讓的LHS實際有差別:

string result = ... // Fails 
var result = ... // Works fine, var is of type LookupCode<object> 
+0

謝謝 - 這很有道理。我也認爲Tamas描述的是同樣的事情,但他提到試圖在隱式運算符中拋出不同的異常是不正確的。 –

-2

的問題是不是三元的右邊的參數評估,它clearnly沒有(試試吧,拋出一個不同的異常中所隱含的運營商,該代碼仍然拋出,因爲((Nullable<byte>)(null)).ValueInvalidOperationException

所以問題的關鍵是什麼時候隱式投射發生。看來:

clientFile.ReviewMonth == null ? null : MonthNames.AllValues[clientFile.ReviewMonth.Value] 

被equvivalent到

(string)(clientFile.ReviewMonth == null ? (Nullable<LookupCode<byte>>)null : (Nullable<LookupCode<byte>>)MonthNames.AllValues[clientFile.ReviewMonth.Value]); 

而不是

clientFile.ReviewMonth == null ? (string)null : (string)MonthNames.AllValues[clientFile.ReviewMonth.Value]); 

所以ReSharper的根本就是錯在這裏。

+0

因爲null是不能隱式轉換爲'bool',我不明白你怎麼能聲稱表達式等同於'(string)(null?...)'。在運算符的左側,'null'文字不合法。 –