程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET,你忘記了麼?(七)—— 從a?b:c談起

.NET,你忘記了麼?(七)—— 從a?b:c談起

編輯:關於.NET

1.摘要

在這篇文章中,我會通過IL去分析一個簡單的語句。

如果覺得實在簡單,可以略過。

2.引子

事情是這樣的,同事寫了一段類似這樣的代碼:

class Program
{
       static void Main(string[] args)
       {
             object o = new object();
             int i;
             Int32.TryParse(Console.ReadLine(), out i);
             o = i > 3 ? null : 3.5;
       }
}

當然不是在控制台程序中,我在這裡只是寫出個模擬。

然後系統報出了一個這樣的錯誤。

3. 錯誤分析

同事很詫異地問我,這是為什麼啊?

他給出的理由是object是一切類的父類,那麼我把3.5或者null賦給他都沒有問題啊,那 這個問題是怎麼回事呢?

我意識到自己的語言表達能力遠不如代碼有說服力,於是,寫段代碼,然後請出IL。

4. 請出IL

讓我們先寫段正確的代碼,保證他的編譯通過。

class Program
{
       static void Main(string[] args)
       {
             object o = new object();
             int i = 1;
             int j = 2;
             o = i+j > 3 ? 3 : 3.5;
       }
}

然後去查看IL代碼:

.method private hidebysig static void Main(string[] args) cil  managed
{
       .entrypoint
       .maxstack 2
       .locals init (
       [0] object o,
       [1] int32 i,
       [2] int32 j)
       L_0000: nop
       L_0001: newobj instance void [mscorlib]System.Object::.ctor()
       L_0006: stloc.0
       L_0007: ldc.i4.1
       L_0008: stloc.1
       L_0009: ldc.i4.2
       L_000a: stloc.2
       L_000b: ldloc.1
       L_000c: ldloc.2
       L_000d: add
       L_000e: ldc.i4.3
       L_000f: bgt.s L_001c
       L_0011: ldc.r8 3.5
       L_001a: br.s L_0025
       L_001c: ldc.r8 3
       L_0025: box float64
       L_002a: stloc.0
       L_002b: ret
}

在這裡,我們只關注從L_00d開始的代碼,首先我們將兩個數i和j相加,然後去與3比較大 小,如果大於3,那麼便跳轉到L_001c,將3作為Float類型壓棧,否則順序向下執行,將3.5 作為float類型壓棧。最後將棧頂元素裝箱。

看過了這個解釋,我們再回去看原有的那段代碼,原因再清楚不過了,?:這個三元運算符 在編譯成IL代碼時,把:兩端的值壓棧,然後把這兩個值存儲在一個臨時變量裡,而這個變 量要取兩者之前類型轉換後級數最高的類型。舉個例子:int 和 float 就需要轉換成float ,float 和 double 就需要轉換成double 等等。而在同事的程序中, double 類型和 null 類型無法相互轉換,所以就報了這樣的一個錯誤。

5. 改造代碼

繼續仔細思考,究竟什麼樣的兩個類型可以寫在:的兩端。上面的錯誤再清楚不過。兩個 可以隱式轉換的類型可以。

那下面繼續解決這個問題。上面的代碼我們要怎麼寫:

static void Main(string[] args)
{
       object o = new object();
       int i;
       Int32.TryParse(Console.ReadLine(), out i);
       if (i > 3)
       {
             o = null;
       }
       else
       {
             o = 3.5;
       }
}

怎麼看都沒有上面的代碼漂亮。這個可以說除了能運行外真的沒什麼優點了。

還記得那個泛型類吧:Nullable<T>。

那就讓我們用這個泛型類來改造吧:

static void Main(string[] args)
{
       object o = new object();
       Nullable<double> n = 3.5;
       int i;
       Int32.TryParse(Console.ReadLine(), out i);
       o = i > 3 ? null : n;
}

如果覺得Nullable<T>還不夠美觀。

static void Main(string[] args)
{
       object o = new object();
       double? n = 3.5;
       int i;
       Int32.TryParse(Console.ReadLine(), out i);
       o = i > 3 ? null : n;
}

這樣改造是不是優秀了一些呢?

這個時候如果有人提出,那麼我為什麼不o=i>3?null:(object)3.5呢?那我們想一下 如果有一天我們不再用object o;

我們是不是可以把代碼寫成這樣:

static void Main(string[] args)
{
   //object o = new object();
   double? o;
   double? n = 3.5;
   int i;
   Int32.TryParse(Console.ReadLine(), out i);
   o = i > 3 ? null : n;
}

ShadowK 給出了這樣的做法,是個好辦法:

o = i > 3 ? (double?)null : n ;

這個時候再看IL,是不是已經沒有了可惡的box呢?

.method private hidebysig static void Main(string[] args) cil  managed
{
   .entrypoint
   .maxstack 2
   .locals init (
     [0] valuetype [mscorlib]System.Nullable`1<float64> o,
     [1] valuetype [mscorlib]System.Nullable`1<float64> n,
     [2] int32 i,
     [3] valuetype [mscorlib]System.Nullable`1<float64> CS$0 $0000)
   L_0000: nop
   L_0001: ldloca.s n
   L_0003: ldc.r8 3.5
   L_000c: call instance void [mscorlib] System.Nullable`1<float64>::.ctor(!0)
   L_0011: nop
   L_0012: call string [mscorlib]System.Console::ReadLine()
   L_0017: ldloca.s i
   L_0019: call bool [mscorlib]System.Int32::TryParse(string,  int32&)
   L_001e: pop
   L_001f: ldloc.2
   L_0020: ldc.i4.3
   L_0021: bgt.s L_0026
   L_0023: ldloc.1 
   L_0024: br.s L_002f
   L_0026: ldloca.s CS$0$0000
   L_0028: initobj [mscorlib]System.Nullable`1<float64>
   L_002e: ldloc.3
   L_002f: stloc.0
   L_0030: ret
}

6. 總結

寫上面的文章我並不是單純地想闡明這個具體的語法情況。而是希望大家掌握一種思路。

語法怎麼回事?為什麼不是像我想的那樣?

請出IL。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved