本文總結C#一些常見的技術問題,每一個都是簡短的解釋,篇幅不大,不斷更新中…
const與readonly
readonly為執行時常量,const為編譯時常量。
編譯時常量比執行時常量快,效能好,但是缺乏靈活性(編譯時常量需要重新編譯應用程式)。
編譯時常量(const)僅限於數值和字串(基元型別),C#不允許使用new來初始化一個編譯時常量
const修飾的常量預設是靜態的(型別)。
readonly修飾的欄位可以在建構函式中被修改。
使用const較之使用readonly的唯一好處就是效能。
partial關鍵字
此關鍵字允許將類、結構或介面的定義拆分到多個檔案中。
如果類的定義,其內容很多,那麼分別放在不同的檔案中就是一個不錯的選擇。
在File1.cs中
1 2 3 4 5 6 7 8 9 |
namespace Aiqier { partial class A { int num = 0; void MethodA() {} partial void MethodC(); } } |
在File2.cs中
1 2 3 4 5 6 7 8 |
namespace Aiqier { partial class A { void MethodB() {} partial void MethodC() {} } } |
sealed關鍵字
當對一個類應用 sealed 修飾符時,此修飾符會阻止其他類從該類繼承。類似於Java中final關鍵字。
new和override
Override關鍵字主要是提供派生類對基類方法的新實現,重寫的基類方法必須和Override的方法具有相同的簽名。
New關鍵字主要用來區別派生類和基類同名方法的選擇問題,通過隱藏基類方法,達到使編譯器呼叫正確的方法的目的。
也就是說New 關鍵字在作為修飾符用於向基類成員隱藏繼承成員時,對於派生類該關鍵字指示方法是重寫的新方法,但是關閉了多型性。具體呼叫的方法為宣告時變數的方法。
C#中using語句怎麼用?
說道using的話,首先要說的就是.Net中的兩種資源,也就是託管資源和非託管資源。
託管資源:由CLR管理分配和釋放的資源,即從CLR裡new出來的物件。
非託管資源:不受CLR管理的物件,如Windows核心物件,檔案,資料庫連線,套接字,COM物件。
這裡要注意,假如說你的型別需要顯式釋放資源,那麼一定要繼承IDispose介面。
而這個IDispose介面就是為using語法糖提供便利,那種在finall處呼叫Dispose函式的try-catch-finally語句塊,其實和using語句生成的IL程式碼基本上完全一致。
c#型別轉換
值型別
值型別的型別轉換,可以理解為用一個型別A的值去初始化一個型別B的變數。
變寬轉換
如果是變寬轉換,那麼不會有問題。比如32位的int到64位的int,或者是int到float。
變窄轉換
如果是變窄轉換,那麼就可能有問題。(可能會發生溢位)
首先,變窄轉換是需要強制型別轉換的,不能像隱式轉換那樣,需要在括號中寫入要轉換的型別。
1 2 |
double foo = 3.5; float bar = (float)foo; |
溢位
比如short可以儲存0~32767的數字,而byte可以儲存的最大值是255,那麼你將一個為7的short轉換為byte型別,那麼不會有什麼問題。但是如果你將一個大於255的short強制型別轉換為byte,那就會有問題。
1 2 3 4 5 |
byte destinationVar; short sourceVar = 281; destinationVar = (byte)sourceVar; // sourceVar val: 281 / destinationVar: 25 |
原因是這樣的(源資料的最左邊一位丟失了):
1 2 3 |
281 = 100011001 25 = 000011001 255 = 011111111 |
這個數值的例子摘自《c#經典入門》
可以使用checked和unchecked關鍵字檢驗溢位。
1 2 |
checked(<expression>) unchecked(<expression>) |
預設就是unchecked的,不寫這個關鍵字也行。如果是checked,而且發生了溢位,就會丟擲異常。
引用型別
引用型別的轉換與值型別“看上去有點相反”。
首先要明確的是引用型別轉換的是棧中的變數,而該變數指向的位於堆中的物件不受影響。
向上轉換
將父類的引用指向子類物件是沒什麼問題的(多型的本質)
向下轉換
與值型別的轉換類似,引用型別的向下轉換也會用到強制型別轉換。
而這種轉換並不總是有效(即便是基類到派生類的轉換),轉換是否成功,只有在執行的時候才會知道。
1 |
Child child = (Child)father; |
最常見的用法是傳遞object物件,然後在將這個得到的物件轉化為要處理的型別,(非泛型集合,傳送訊息機制)
as運算子
強制型別轉換出現錯誤會丟擲異常,使用as
或is
會更加優雅。
as運算子用於在兩個引用型別之間轉換,轉換失敗後會返回一個null,並不會丟擲異常。
1 2 3 4 5 6 7 |
ClassA a = new ClassA(); ClassB b = a as ClassB; if(b != null) { ...... } |
無論是as還是is運算子,都比直接使用強制型別轉換要安全,而且不需要使用異常檢查,只需要判斷結果是否為空就可以了。
型別無關的型別轉換
之前提到的引用型別轉換,是指相關型別之間的轉換,比如繼承關係,共享介面。
不相關的兩個型別,也可以發生型別轉換,這就要使用到過載運算子,你需要自己定義內部轉換的原理。
需要使用到的過載運算子有: implicit(隱式型別轉換) explicit(顯示型別轉換)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// 定義ConvClassA到ConvClassB的隱式轉換 public class ConvClassA { public static implicit operator ConvClassB(ConvClassA op) { ...... } } // 定義ConvClassB到ConvClassA的顯式轉換 public class ConvClassB { public static explicit operator ConvClassA(ConvClassB op) { ...... } } |