C#部分特殊符號和關鍵字使用小技巧


  在程序中需要進行判斷的時候,我們經常使用分支結構,如if-else和switch-case分支結構,當然,有時候我們也和循環體聯合、多重分支結構嵌套使用以實現復雜判斷。

  當然,如大多數朋友所熟知,如果只有簡單的判斷(即情況只有‘是’和‘否’兩種),且需要通過判斷進行變量賦值時,我們可以使用“三段式”(即在變量賦值語句中使用如下語法)完成。

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test1
6 {
7 static void Main(string[] args)
8 {
9 // 在如下代碼中完成字符串"strTest"的賦值
10        // 語句由 類型 變量名 = 判斷條件 ? 可賦值1 : 可賦值2; 組成
11        // 如果判斷條件成立變量被賦值為可賦值1,反之為可賦值2
12 string strTest = 1 > 2 ? "1大於2成立" : "1大於2不成立";
13 Console.WriteLine(strTest);
14 Console.ReadKey();
15 // 輸出結果為 "1大於2不成立"
16        // 如下寫法錯誤
17        // 1 > 2 ? Console.WriteLine("1大於2成立") : Console.WriteLine("1大於2不成立");
18 }
19 }
20 }

  通過這種“三段式”,可以簡化我們程序的編碼,也是思路和邏輯更加清晰。

  在實際編碼中,我們為了減少異常拋出率,增加程序的健壯性(即代碼的嚴謹性),時常在引用類型的使用之前進行是否為空的判斷,在以前使用if-else判斷,使代碼顯得冗余或者麻煩,那么現在我們可以使用“三段式”進行判斷。

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test2
6 {
7 static void Main(string[] args)
8 {
9 string strTest1 = null;
10 string strTest2 = strTest1 == null ? "將要被賦的值為空" : "將要被賦的值不為空";
11 // 以下的寫法也可以,調用string類的靜態方法判斷參數字符串是否為空、空字符串或以空白字符組成
12        // 也可以用string.IsNullOrEmpty(string value)方法,可以判斷參數字符串是否為空、空字符串組成
13        //string strTest2 = string.IsNullOrWhiteSpace(strTest1) ? "將要被賦的值為空" : "將要被賦的值不為空";
14 Console.WriteLine(strTest2);
15 Console.ReadKey();
16 // 輸出結果為 "將要被賦的值為空"
17
18        // 條件簡單時,可以不將條件用括號提升優先級、保證完整
19        // 當然,也可以在為空的情況下為變量賦默認值
20        // 如下
21        // string strTest2 = (strTest1 == null) ? "將要被賦的值為空,這是默認值" : strTest1;
22        // 輸出結果為 "將要被賦的值為空,這是默認值"
23 }
24 }
25 }

  但是這樣看起來沒有顯著地減少代碼量是吧?別急,當然有符號可以滿足我們對代碼更簡練的要求,它可以在判斷為空並賦默認值的時候大發神威哦,比如下面的示例。

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test3
6 {
7 static void Main(string[] args)
8 {
9 string strTest1 = null;
10 string strTest2 = strTest1 ?? "將要被賦的值為空,這是默認值";
11 // 以上語句完全等同於如下代碼
12        // string strTest2 = strTest1 == null ? "將要被賦的值為空,這是默認值" : strTest1;
13 Console.WriteLine(strTest2);
14 Console.ReadKey();
15 // 輸出結果為 "將要被賦的值為空"
16 }
17 }
18 }

  是不是很方便?為變量賦值時在等號右邊給出兩個可選值,用??隔開,如果??左邊的值不為空,則將??左邊的值賦給變量,否則就將右邊的值賦給變量,即賦默認值。

  當然,我經常在公開屬性里使用此語句,如下。

 1 using System;
2 using System.Collections.Generic;
3
4 namespace EGForSomeSmallSkills
5 {
6 class Test4
7 {
8 private string _name;
9
10 public string Name
11 {
12 // 在訪問器的讀取器中使用,如果_name為空值,返回默認的值
13 get { return _name ?? "Johness"; }
14 set { _name = value; }
15 }
16
17 // 此語句在如下訪問器中尤其有效
18 private List<string> _list;
19
20 public List<string> List
21 {
22 // 對於比較不細心的朋友來說
23        // 這里加上一句話有助於減少異常
24 get { return _list ?? new List<string>(); }
25 set { _list = value; }
26 }
27
28 // 當然,這是治標不治本的方法
29     // 最好的解決辦法是在構造方法實體內對引用類型進行必要的初始化
30      // 如下
31 public Test4()
32 {
33 _list = new List<string>();
34 // 我一般即時在類內部也盡量使用字段的對外屬性
35        // 即這樣書寫
36        // List = new List<string>();
37        // 通過構造方法參數等對引用類型屬性進行初始化
38        // ……
39 }
40 }
41 }

  下面我們要介紹另一個強大的符號,?。使用它,我們可以在需要的情況下使值類型可空引用,即使值類型的值可以引用空,可以判斷是否為空,請看下面的代碼。

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test5
6 {
7 public static void Main(string[] args)
8 {
9 // int i = null;
10        // 一般情況下,以上代碼是違反C#(其他大部分語言是一樣)規范的
11        // 編譯無法通過會產生“無法將 Null 轉換成“int”,因為它是一種不可以為 null 值的類型”的錯誤
12        // 但是一下代碼是可以的!
13 int? i = null;
14 // 在值類型聲明時在聲明的類型后添加?,時被聲明值類型擁有部分引用類型的特質
15        // 比如可為空、可比較為空
16
17 Console.WriteLine(i == null);
18 // 輸出“True”
19
20        // 但是,在這樣做了之后,又有了一個難題:int?類型並不是int了,原來取得其值的方法(如下)
21        // int j = i;
22        // 就會發生錯誤,這是因為類型不同了
23        // 我們希望取得int?型的值可以用如下表達式
24 int j = i.Value;
25 // 當然,如果int?為空的話,它對應的value也是為空
26        // 現在如果使用j,會發生異常
27 }
28 }
29 }

  通過以下例子,看看System.Nullable<T>,即值類型加?的實際應用(注:參考http://msdn.microsoft.com/zh-cn/library/b3h38hb0.aspx(Nullable(Of T)結構

))。

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test6
6 {
7 public static void Main(string[] args)
8 {
9 Test(0.3, 100);
10 // 輸出 “是會員 應付金額:30”
11 Test(null,100);
12 // 輸出 "不是會員 應付金額:100"
13 }
14
15 /// <summary>
16      /// 一下方法演示通過傳遞的“折扣”是否為空判斷方法調用者是否為VIP
17      /// </summary>
18      /// <param name="discount">折扣,在本示例中表示VIP的折扣
19      /// 為0到1的小數
20      /// 如果傳遞null,則表示為普通用戶,沒有打折
21      /// </param>
22      /// <param name="money">原價</param>
23 public static void Test(double? discount, double money)
24 {
25 double discountR = discount ?? 1; // 保存折扣,如果不是VIP則折扣率為1
26 double pay = money * discountR; // 實際應付金額
27 string outPutStr = (discount == null ? "不是會員" : "是會員") + " 應付金額:" + pay; // 連接語句並輸出
28 Console.WriteLine(outPutStr);
29 Console.ReadKey();
30 }
31 }
32
33 }

  接下來我們說一說new,這個東西我們平時用來創建對象新示例的關鍵字其實還可以作為修飾符使用,而我們現在主要研究一下new作為修飾符使用,先看看下面的例子。

  總所周知,在類的繼承里,JAVA奉行所有的父類方法都可以被子類覆蓋重寫,即所有方法都是C#里對應的虛方法。那么這樣其實可能會造成效率的損失,而在C#里有實例方法,但是我們如何在父類示例方法的基礎上進行擴展,即覆蓋重寫父類實例方法呢?

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test7_Pro
6 {
7 public static void Main(string[] args)
8 {
9 Test7_Child child = new Test7_Child(); // 創建子類對象
10 Test7_Base bas = child; // 用父類引用保存子類對象
11 Test7_Base basR = new Test7_Base(); // 創建父類對象
12
13 basR.ViMethod(); // 調用父類對象的虛方法
14 bas.ViMethod(); // 調用父類引用保存子類對象的重寫的虛方法(沒有重寫即調用父類虛方法)
15 (bas as Test7_Child).ViMethod(); // 調用子類重寫自父類的虛方法 即 child.ViMethod();
16
17 basR.EnMethod(); // 調用父類實例方法
18 bas.EnMethod(); // 調用父類實例方法(不會涉及到子類的方法)
19 (bas as Test7_Child).EnMethod(); // 調用子類與父類實例方法重名的方法
20
21        // 輸出結果
22 /* 執行了父類的虛方法!
23 執行了子類重寫自父類的虛方法!
24 執行了子類重寫自父類的虛方法!
25 執行了父類實例方法!
26 執行了父類實例方法!
27 執行了子類與父類實例方法同名的方法!*/
28 }
29 }
30
31 class Test7_Base
32 {
33 // 父類虛方法,用於實現多態,可以被子類重寫
34 public virtual void ViMethod()
35 {
36 Console.WriteLine("執行了父類的虛方法!");
37 }
38 // 父類示例方法,屬於父類而不屬於其派生類
39 public void EnMethod()
40 {
41 Console.WriteLine("執行了父類實例方法!");
42 }
43 }
44
45 class Test7_Child : Test7_Base
46 {
47 // 重寫父類虛方法
48 public override void ViMethod()
49 {
50 Console.WriteLine("執行了子類重寫自父類的虛方法!");
51 }
52 // 使用new修飾符向父類隱藏與父類沖突的實例方法
53      // new和public的順序可以交換
54 public new void EnMethod()
55 {
56 Console.WriteLine("執行了子類與父類實例方法同名的方法!");
57 }
58 }
59
60 }

  new作為修飾符主要是向父類隱藏同名成員,在接口也可以用。

  在作為對象聲明時,new關鍵字還有着更為重要的作用,上一次隨筆我就匿名委托(或匿名方法)做了淺淺的分析,這次介紹.NET3.0和4.0的新特性:匿名類型和動態類型。(注:參考http://msdn.microsoft.com/zh-cn/library/bb397696.aspx(匿名類型(C# 編程指南))http://msdn.microsoft.com/zh-cn/library/dd264736.aspx(使用類型 dynamic(C# 編程指南)))

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test8
6 {
7 public static void Main(string[] args)
8 {
9 var v = new { Name = "Johness" }; // 定義匿名類型變量
10 Console.WriteLine("Hello," + v.Name); // 使用該對象
11        // 輸出“Hello,Johness”
12        // 在.NET4.0的Linq查詢中會大量使用匿名類型
13 }
14 }
15 }

  在我們的編碼中也可以使用匿名類型的屬性賦值方式,如下:

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test9_Pro
6 {
7 public static void Main(string[] args)
8 {
9 Test9 t91 = new Test9(19, "Johness");
10 // 相比參數較多帶來的傳遞不便,下面的代碼簡潔且清晰
11 Test9 t92 = new Test9 { Name = "Johness", Age = 19 };
12 // 也可以顯示調用帶參構造方法並在其后使用類似匿名類型聲明方式完成屬性初始化
13        // 注意:只讀域不能在如下{}中聲明,但可以在構造方法體中初始化,如下
14 Test9 t93 = new Test9(5) { Name = "Johness", Age = 19 };
15 Console.WriteLine(string.Format("年齡:{0} 姓名:{1}", t91.Age, t91.Name));
16 Console.WriteLine(string.Format("年齡:{0} 姓名:{1}", t92.Age, t92.Name));
17 Console.WriteLine(string.Format("年齡:{0} 姓名:{1}", t93.Age, t93.Name));
18 // 輸出
19        // 年齡:19 姓名:Johness
20        // 年齡:19 姓名:Johness
21        // 年齡:19 姓名:Johness
22 }
23 }
24
25 class Test9
26 {
27 private readonly int i;
28 public int Age { get; set; }
29 public string Name { get; set; }
30 // ……
31
32 public Test9(int age, string name) // 在屬性較多並且需要在構造對象時初始化的情況下,我們可能會對構造方法進行重載
33 { // 但是即時經過重載,我們傳遞較多參數時仍可能混淆先后次序,造成不便
34 this.Age = age;
35 this.Name = name;
36 }
37
38 public Test9(int i) // 傳遞只讀屬性初始值進行只讀域初始化
39 {
40 this.i = i;
41 }
42
43 public Test9()
44 {
45
46 }
47 }
48 }

  動態類型和匿名類型的差別有兩點:一是匿名類型的真正類型只能指定一次,而動態類型不是,二是匿名類型是.NET3.0新特性,而動態類型是.NET4.0新特性……開玩笑的,不過在以下實例中可以真正看到他們的第二點不同:

 1 using System;
2
3 namespace EGForSomeSmallSkills
4 {
5 class Test10
6 {
7 public static void Main(string[] args)
8 {
9 // 可以將匿名類型轉化為對象類型返回
10 object obj1 = GetSomeObjectByVar();
11 // 但系統無法識別它的真正類型
12        //Console.WriteLine(obj1.Name);
13
14 dynamic obj2 = GetSomeObjectByDynamic();
15 Console.WriteLine(obj2.Name);
16
17 // 輸出“Johness”
18 }
19
20 public static object GetSomeObjectByVar()
21 {
22 var someObj = new { Name = "Johness", Age = 19 };
23 return someObj;
24 }
25
26 // 由於是動態類型,返回值可以為object、dynamic
27 public static dynamic GetSomeObjectByDynamic()
28 {
29 // dynamic 也可以替換為var、object
30 dynamic someObj = new { Name = "Johness", Age = 19 };
31 return someObj;
32 }
33 }
34 }

  這次的小技巧就到這兒。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM