1. 屬性的自動實現
- 在 C#3 以前,每個屬性需要手動實現,也就是需要手動為屬性添加 get 訪問器與 set 訪問器,如:
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
- 在 C#3 以後,可以透過自動實現的方式撰寫:
- 其中命名為
name
的變數由編譯器自動創建並為其賦予名稱。
- 其中命名為
public string Name { get; set; }
- 但在 C#3 時,不能宣告
readonly
的自動屬性,且不能在宣告時賦予初始值,在 C#6 做了修正。在 C#3 只能透過訪問子分離來模擬readonly
:
public string Name { get; private set; }
2. 隱式類型(var)
靜態類型 vs. 動態類型:
- 靜態類型是面向編譯的的語言,故所有類型由編譯器決定,在編譯期就會完成綁定。
- 動態類型則是延遲綁定的時間,在執行期才進行類型綁定。
顯式類型 vs. 隱式類型:
- 顯式類型代表在程式碼表達式便給出具體的類型訊息。
- 隱式類型則允許了類型推斷,透過上下文判斷出類型.
C#3 開始支援隱式類型的寫法,關鍵字是
var
。var name = "Rainhu"; // string name = "Rainhu"
- 隱式類型的宣告有幾個限制:
- 變數必須在宣告時就初始化。
- 用於初始化的表達式必須已具備某個類型。
- 只能用於局部變數
var x; x = 10; // 違反1 var y = null; // 違反2
- 特別注意到:C# 是在編譯期就完成隱式類型的綁定
- 隱式類型的宣告有幾個限制:
隱式類型因為其方便性,但很容易造成濫用,其適用的情形大致如下:
- 變數為匿名類型,不能為其指定類型。
- 變數的型別過長,並且初始化表達式就足以用來推斷。
- 變數的精確類型不重要,並可以透過初始化表達式來推斷。
Dictionary<string, List<decimal>> mapping = new Dictionary<string List<decimal>>(); var mapping = new Dictionary<string, List<decimal>>();
隱式類型的數組
int[] arr = new int[10]; int[] arr1 = { 1, 2, 3, 4, 5 }; int[] arr2 = new int[] { 1, 2, 3, 4, 5 }; int[] arr3; arr3 = {1, 2, 3, 4, 5} // 非法 arr3 = new[] { 1, 2, 3, 4, 5 };
物件與集合的初始化
- 以電子商務系統為例
public class Order { private readonly List<OrderItem> items = new List<OrderItem>(); public string OrderId { get; set; } public Customer Customer { get; set; } public List<OrderItem> Items { get { return items; }} public void Add(OrderItem item) { items.Add(item); } } public class Customer { public string Name { get; set; } public string Address { get; set; } } public class OrderItem { public string ItemId { get; set; } public int Quantity { get; set; } }
- 若不使用物件初始化器與集合初始化器便會像:
var customer = new Customer(); customer.Name = "Mike"; customer.Address = "Japan"; var item1 = new OrderItem(); item.ItedId = "item12345"; item.Quantity = 1; var item2 = new OrderItem(); item.ItedId = "item54321"; item.Quantity = 2; var order = new Order(); order.OrderId = "order12345"; order.Customer = customer; order.Items.Add(item1); order.Items.Add(item2);
- 若使用物件初始化器與集合初始化器,便可大大增加可讀性,並且在接下來的 LINQ 上有大大的好處:
var order = new Order { OrderId = "order12345", Customer = new Customer { Name = "Mike", Address = "Japan" }, Items = { new OrderItem { ItemId = "item12345", Quantity = 1 }, new OrderItem { ItemId = "item54321", Quantity = 2 } } }
3. 匿名類型
- 前面說到隱式類型,好像是可有可無的存在,但在匿名類型,就是一個只能用隱式類型來宣告的情況了。
- 匿名類型的宣告:
var procuct { Name = "CD Pro-2", Price = 50000 } string name = product.Name; int price = product.Price;
- 搭配隱式類型數組的匿名類型宣告:
var products = new[] { new { Name = "CD Pro-2", Price = 50000 }, new { Name = "iDream", Price = 100000 }, new { Name = "Forsung TV", Price = 150000 } }
4. lambda 表達式
基本語法
- lambda 表達式的基本語法為:
參數列表 => 主體
- 與之前的匿名方法類似,只是將
delegate
換成了=>
Action<string> action = (string msg) => { Console.WriteLine(msg); }; action("hello world!");
- 但在主體包含的內容很短時,可以簡化成:
Action<string> action = (string msg) => Console.WriteLine(msg);
- 參數列表若可以經由類型推斷,那麼可以進一步簡化成:
Action<string> action = (msg) => Console.WriteLine(msg);
- 參數列表只有一個參數,括號也可以省略:
Action<string> action = msg => Console.WriteLine(msg);
- 與之前的匿名方法類似,只是將
捕獲變數
- 對開發者而言,lambda 表達式除了可以使用自於的參數列表外,還可以捕捉靜態變數、物件實例、
this
變數、方法參數或局部變數,後者全部都為捕獲變數,而根據不同的作用域,編譯器會為其編譯成相對應的 IL code。- 在此提供一個以生成類來實現的例子:
public Action<string> CreateAction(string methodParameter) { string methodLocal = "method Local"; string uncaptured = "uncaptured"; Action<string> action = lambdaParameter => { string lambdaLocal = "lambda local"; Console.WriteLine("Instance field: {0}", instanceField); Console.WriteLine("Method Parameter: {0}", methodParameter); Console.WriteLine("Method Local: {0}", methodLocal); Console.WriteLine("Lambda parameter: {0}", lambdaParameter); Console.WriteLine("Lambda local: {0}", lambdaLocal); }; methodLocal = "modified method local"; return action; }
- 經轉譯後的程式碼
private class LambdaContext { public CapturedVariable original; public string methodParameter; public string methodLocal; public void Method(string lambdaParamter) { string lambdaLocal = "lambda local"; Console.WriteLine("Instance field: {0}", instanceField); Console.WriteLine("Method Parameter: {0}", methodParameter); Console.WriteLine("Method Local: {0}", methodLocal); Console.WriteLine("Lambda parameter: {0}", lambdaParameter); Console.WriteLine("Lambda local: {0}", lambdaLocal); } } public Action<string> CreateAction(string methodParameter) { LambdaContext context = new LambdaContext(); context.original = this; context.methodParameter = methodParameter; context.methodLocal = "method Local"; string uncaptured = "uncaptured local"; Action<string> action = context.Method; context.methodLocal = "modified method local"; return action; }
5. 擴展方法 Extension Method
- 擴展方法是一種靜態方法,可以對其第一個參數的類型物件以物件的方式進行調用:
SampleClass.Method(x,y); x.Method(y);
- 宣告方式:
using System; namespace Battle.Extensions { public static class BattleExtensions { public static void Attack(this Warrior Alpha, Warrior Delta) { Battle.Attack(Alpha, Delta); } } }
- 調用方式
Warrior Alpha = new Warrior { Name = "Alpha", Level = 5, Attack = 10, HP = 20 }; Warrior Delta = new Warrior { Name = "Delta", Level = 5 Attack = 8, HP = 25 }; // 原始版本 Battle.Attack(Alpha, Delta); // 擴充方法版本 Alpha.Attack(Delta);
- 編譯器唯一需要做的是為擴展方法及其所在類添加
[Extension]
特性。該特性在命名空間System.Runtime.CompileServices
下。本質上是一個標記,標記擴充方法。
6. LINQ 查詢
- 接著我們可以透過 lambda 表達式與擴充方法來進行鏈式調用:
Warrior Alpha = { Name = "Alpha", Level = 5, Attack = 10, HP = 20 }; Warrior Beta = { Name = "Beta", Level = 3, Attack = 6, HP = 12 }; Warrior Gamma = { Name = "Gamma", Level = 5, Attack = 12, HP = 18 }; Warrior Delta = { Name = "Delta", Level = 5, Attack = 8, HP = 25 }; Warrior[] warriors = { Alpha, Beta, Gamma, Delta }; IEnumerable<string> query = warriors .Where(warrior => warrior.Level >= 5) .OrderBy(warrior => warrio.Name) .Select(warrior => warrior.Name.ToUpper()); // ["ALPHA", "DELTA", "GAMMA"]
- 若沒有使用
IEnumerable
的擴充方法,會長這樣,可讀性整個就下降了:
Warrior[] warriors = { Alpha, Beta, Gamma, Delta }; IEnumerable<string> query = Enumerable.Select( Enumerable.OrderBy( Enumerable.Where(warriors, warrior => warrior.Level >= 5), warrior => warrior.Name, ), warrior => warrior.Name.ToUpper());
- LINQ 還可以使用查詢表達式:
IEnumerale<string> query = from warrior in warriors where warrior.Level >= 5 orderby warrior.Name select warrior.Name.ToUpper();
- 甚至可以利用
let
引入新的變數
IEnumerale<string> query = from warrior in warriors let name = warrior.Name where warrior.Level >= 5 orderby name select name.ToUpper();
- 若沒有使用