2012-11-14 48 views
2

我們使用?? Operator來評估對空值的表達式,例如:其中一期工程快空聚結,三元或聲明如果

string foo = null; 
string bar = "woooo"; 
string foobar= foo ?? bar ; 
// Evaluates foobar as woooo 

我們還使用了if語句,該語句的工作方式相同,如果用上述表達

使用
string foo = null; 
string bar = "woooo"; 
if(foo==null) 
    string foobar= "woooo" ; 
// Evaluates foobar as woooo same as above 

而且還?: Ternary Operator ...

string foo = null; 
string bar = "woooo";  
string foobar= foo==null ? "woooo" : null ; 
// Evaluates foobar as woooo same as above 

我知道空合併在語法上是精確的,但哪一個在兩者之間編譯得更快,執行速度更快,爲什麼?

+0

它更像是語法糖.. – Amitd

+1

如果您是關注運行時,C#可能不你最好的選擇。 –

+0

從技術上講,你應該把'??'與'?'比較:''不要'if'作爲'?:'比'if'更接近'''更合乎邏輯。 –

回答

14

您可能會問錯的問題。您不會選擇使用其中一種,主要是因爲效率(儘管它可能是次要問題),但是由於效用。

真的,你應該比較???:而不是if,因爲它們有不同的目的。是的,他們都是某種形式的「有條件的」善,但關鍵是???:評估爲一個值,而if沒有,因此他們經常有不同的用途。

例如,下面的代碼:

Console.WriteLine("The order} is for {1} product", 
    orderId, productId ?? "every"); 

將是笨重與if寫:

if (productId == null) 
{ 
    Console.WriteLine("The order {0} is for every product", 
     orderId); 
} 
else 
{ 
    Console.WriteLine("The order {0} is for {1} product", 
     orderId, productId); 
} 

是的,你可能會凝結成一個,但隨後你就會有一個臨時變量等:

if (productId == null) 
{ 
    productId = "every"; 
} 

Console.WriteLine("The order {0} is for {1} product", 
    orderId, productId); 

因此,真的,你不應該比較這兩個,因爲它們的指針如果參數是null,則??的t值將計算爲一個值,而if的值則執行不同的路徑(不直接生成值。

所以,一個更好的問題可能是,爲什麼不是這個:

Console.WriteLine("The order {0} is for {1} product", 
    orderId, productId == null ? "every" : productId); 

這是大致相同(均爲評估的值),並沒有那麼多的流量控制。

因此,我們來看看其中的差異。讓我們寫這樣的代碼三種方式:

// Way 1 with if 
string foo = null; 
string folder = foo; 

if (folder == null) 
{ 
    folder = "bar"; 
} 

// Way 2 with ? : 
string foo2 = null; 
var folder2 = foo2 != null ? foo2 : "bar"; 

// Way 3 with ?? 
string foo3 = null; 
var folder3 = foo3 ?? "bar"; 

爲IF,我們得到以下的IL:

IL_0001: ldnull  
IL_0002: stloc.0  
IL_0003: ldloc.0  
IL_0004: ldnull  
IL_0005: ceq   
IL_0007: ldc.i4.0  
IL_0008: ceq   
IL_000A: stloc.1  
IL_000B: ldloc.1  
IL_000C: brtrue.s IL_0016 
IL_000E: nop   
IL_000F: ldstr  "bar" 
IL_0014: stloc.0  

對於條件(:)我們得到以下IL:

IL_0001: ldnull  
IL_0002: stloc.0  
IL_0003: ldloc.0  
IL_0004: brtrue.s IL_000D 
IL_0006: ldstr  "bar" 
IL_000B: br.s  IL_000E 
IL_000D: ldloc.0  
IL_000E: nop   
IL_000F: stloc.1 

對於無漏白(??)我們得到這個IL:

IL_0001: ldnull  
IL_0002: stloc.0  
IL_0003: ldloc.0  
IL_0004: dup   
IL_0005: brtrue.s IL_000D 
IL_0007: pop   
IL_0008: ldstr  "bar" 
IL_000D: stloc.1  

請注意每個後續步驟如何更簡單? if是較大的IL,因爲它需要分支邏輯來處理單獨的語句。 ?:較小,因爲它只是計算一個值(不支持其他語句),但仍然需要加載操作數以與(null)進行比較。

??是最簡單的,因爲那裏是比較null(VS裝載null和比較它的IL指令。

SO這一切說,你說一個非常小不管怎樣,這可能會或可能不會影響性能。無論如何,這將有很大的差別,與程序(數學,數據庫,網絡等)中的更多密集工作相比,幾乎沒有什麼區別。因此,我會建議選擇那個大多數是可讀的,並且只有在通過分析發現您當前的方法不足且存在瓶頸時纔會進行優化。

對我來說,使用?:??的真正原因是當您希望最終結果是一個值時。也就是說,任何時候你會忍不住寫:

if (someCondition) 
    x = value1; 
else 
    x = value2; 

然後我會使用條件(?:),因爲這是它是一個偉大的簡寫。 x得到一個或基於此條件下,其他值...

然後我會走得更遠一個與??和說的一樣是真的,你要分配一個值基礎上,null -ness的變量一個身份識別者。

所以if是偉大的流量控制,但如果你只是返回兩個值中的一個或分配基於一個條件的兩個值中的一個,我會使用?:??適當。

最後記住這些東西是如何蓋(IL和相關的性能)下執行受與.NET Framework的每個修訂改變(因爲我現在寫這個權利,他們都如此接近可以忽略不計)。

因此,明天可能會更快的可能不會更快。再說一次,我只是說一個最合適的,你發現最可讀的。

UPDATE

順便說一句,對於真正的癡迷,我比較了每個代碼樣本的10,000,000迭代以上,而這裏的執行每個的總時間。貌似??是最快的,對我來說這個數字也只是所以接近是幾乎無關緊要......

10,000,000 iterations of: 
?: took: 489 ms, 4.89E-06 ms/item. 
?? took: 458 ms, 4.58E-06 ms/item. 
if took: 641 ms, 6.41E-06 ms/item. 
+0

我不得不把foobar分配給一些值,就像我在上面的例子中提到的那樣。因此我在我的場景中使用了'if'。使用三元我沒有找到合適的.. –

+0

way1和way3是一樣的(C#代碼) – comecme

+0

抱歉,剪切和粘貼錯誤,糾正... –

5

它們都應該編譯到相同的IL,並因此執行相同的操作。

即使他們沒有,性能差異也可以忽略不計。除非我對我的應用程序進行了描述,並且看到這些陳述需要花費大量的時間,否則我甚至不會去考慮這一點。

請務必使用最清晰的語法,除非您能證明它是導致性能問題的原因之一。

+1

可能的重複我同意使用最清晰語法的部分,但''''運算符實際上編譯成更少的IL。 –

+2

@JamesMichaelHare - 真的嗎?這是令人驚訝的。不幸的是我沒有在我面前的東西,我可以看看...有什麼區別? –

+0

@JustinNiessner http://pastebin.com/DqXqk3Nm(禁用優化) – asawyer