又好久沒有更新部落格了,還活著還有呼吸!!
最近參加了Will保哥的課程,課後整理一些C#開發中令人困惑的地方,希望分享的同時也讓自己更加印象深刻。
C# 開發中要知道的特性
匿名型別(Anonymous types)
匿名型別是從C#3.0開始出現的
- 將一組唯讀屬性封裝為一個物件,不須事先明確定義類型。
- 類型名稱會由編譯器產生,並且無法於編譯時期使用。
- 每個屬性的類型會由編譯器推斷,不能給予null作為初始值。
- 可以搭配物件初始設定使用new 運算子來建立匿名型別。
好的了解這些我們來看一段code
var human = new { Name = "John", Age = 20 };
human.Name = "Jane";
Console.WriteLine(human.Name + human.Age);
這段code是否會再編譯時期發生錯誤?
是會的在這段human.Name = “Jane”;
原因是匿名型別是唯讀的,new出來的物件是唯讀的不可變的。
在compiler的時期就會有錯誤。
這樣的特性代表狀態不會改變,在一些情況下是很好的,代表它永遠不會被改壞,可以很放心的使用。
它與常數又是不太一樣的,常數只能用實質型別,這邊是類別是參考型別。
如果對參考型別 和 實質型別 不清楚的話可以參考我之前寫過的文章 — C# .NET 中的 淺拷貝(Shallow Copy) 和 深拷貝(Deep copy)
匿名型別是否擁有類別名稱
有的,但它無法被使用,因為是編譯器幫忙產生的,因此它也是強行別的!
我們來看一下LinQPad Run實際編譯出來的結果
在編譯成組合語言的時候它是<>f__AnonymousType0()的型別的。
那我們怎麼知道它是強行別的呢?
在這邊編譯器其實是可以判斷出匿名型別是有哪些屬性,代表它是強行別的。
Lambda Expression 可否使用var 做隱含型別宣告?
我們在使用LinQ時會大量的使用到Lambda Expression 那它可以使用Var宣告嗎??
是可以的但這是C#10(.NET6)以後才有的特性!
如果在C#10之前要拉出來當作一個變數的話會很麻煩,會是一個很不好看的型別。
Nullable Reference Type ? 到底是什麼
nullable 是用來解決 null reference exception的問題,相信有寫C#的開發人員都一定有遇過這個例外。
這也是微軟一直想致力於解決的不停地加入新的語言特性試圖讓開發人員在編譯時期就發現null reference exception,而不是在專案運行期間炸裂了才發現有這個狀況!
C#1.0開始
- 參考型別用於存放物件的參考,因此預設可為null
- 實質型別用於存放物件的值,因此預設不可為null
但這樣的特性有些不彈性,實質型別也有可能沒有值的時候,初始化時我們硬塞一個0給它,在一些狀況下是不合適的。
C#2.0開始
- 加入了 Nullable value types的機制,讓實質型別也可為null
- 使用Nullable 型別時,需要在實質型別後面加上?來表示該型別可以為null,如int?表示一個可為null的整數型別。
讓我們來看一下code
一定要給予一個值,不然在編譯時期就會出錯了。
改為Nullable型別後就可以賦予空值
取值時要改為使用.Value 或是 .GetValueOrDefault()
C#8.0(.NET Core 1.0) 開始
- 讓參考型別也能有null型別
- 啟用Nullable value types的機制後,所有的參考型別預設都是不可為null的,如果可能為null要像Nullable那樣宣告,需要在參考型別後面加上?來表示該型別可為null,例如 string? 表示一個可為null的字串類型。
我們來看一下在專案中的類別
會出現很多綠色的蚯蚓提示,很多開發人員可能不習慣或是直接忽略提示,但其實它是在提醒你避免出現null reference exception。
有沒有人在專案中看過有這樣的寫法呢?這其實不是很好的方法,這樣的寫法是叫編譯器完全不要來檢查,這也會讓往後寫Code時發生非常糟糕的狀況,如果軟體開發有因果業力輪迴的話,這樣是危害蒼生會墮入十八層地獄的行為,請不要埋地雷造孽,對別人不好也對自己不好。
讓我們舉例一下在專案開發中會發生的例外狀況
我們在編譯期間完全不會有問題,但在執行期間就會發生例外了!
這是一個問題我們要如何避免呢?
當我們使用Nullable value types後在編譯時期就會警告我們可能會出問題
最好的方式是使用 Pattern matching 來判斷
這樣警告就完全不見了,而且也相當的安全。
特別是在我們API要與對方對接時,我們定義一個Class 的時候,不確定是否會有空值時建議都是使用Nullable的型別。
讓人困惑的!. 以及 ?. 語法(Null-Conditional Operator)
這些C#新的語法糖(Syntax Sugar)真的會讓第一次看到的人很困惑。
我們來一個一個看一下
Null Coalescing Operator
string s1 = null;
string s2 = s1 ?? "Hello! World";
Console.WriteLine(s2);
??
意思是 s1 如果為空值,就會將 Hello! World 回傳給s2
Null Coalescing Assignment Operator
??=
意思是當s1為空值時會把值賦予給s1,否則會跳過這個值。
Null – Conditional Operators
?.
?.的意思是只要s1為空值後面的方法就不會執行,會直接把null給予s2
是一個比較安全的操作方法,否則會出現例外。
?[0]
是element access,只要需要用index去操作的陣列或集合物件,都可以在變數名稱後面加?
這樣使得調用方法的時候是null也不會執行,也是相對安全的操作方法。
null-forgiving operator
!是允許null運算子,可以使得編譯器不來檢查
這可能會有點反人類但某些狀況下可能會使用到
- 在某些情況下我們根本不關心是否為null
- 在單元測試的時候我們想傳遞null值
Var 與 dynamic 使用的時機
C#3.0
- 隱含型別區域變數
- 宣告區域變數時可以讓編輯器從初始化運算式推斷變數的型別。
以前的寫法我們會是這樣 Customer customer = new Customer();
var的初衷是利用右邊來推斷左邊的型別,我們在開發的時候經常都會很喜歡用var。
但有些情況下是需要盡量避免的,比如右邊是呼叫一個方法,我們從Source Code是無法判斷回傳的型別是什麼的,這樣會導致可讀性下降,需要有開發工具才知道是什麼型別,再提PR時會讓MENTOR或是技術長難以去讀懂你的code是否合理。
C#9.0(.Net5)
Target-typed new expressions
是由左邊去推斷右邊的型別。
dynamic 動態型別
- dyamic 型別指出使用變數和其成員的參考,會略過編譯時期型別的檢查
- 任何非 Null運算式都可以轉換為dyamic型別
dyamic 會讓所有的型別檢查被停用,盡量避免用dyamic進行宣告
會使用dyamic型別的最主要的情況是去呼叫操及複雜的API,我們需要定義一大堆型別以及code的時候去使用那個物件的情況下才會去使用到dyamic。
OK那麼這次的分享主要到這邊,也是替自己做一個學習的紀錄,倘若對你專案上也有遇到類似的問題,或是寫C#有什麼好玩有趣的知識、心得也歡迎分享給我~