C# 進階語法
C# 進階語法
快捷鍵
- main方法:svm + tab
- 構造方法:ctor + tab
其他關鍵詞/修飾符
IntPtr
一種特定於平臺的型別,用於表示指標或控制代碼。
SafeHandler
表示作業系統控制代碼的包裝類。這個類必須被繼承。
readonly
- 在欄位宣告中, 指示只能在宣告期間或在同一個類的建構函式中向欄位賦值。 可以在欄位宣告和建構函式中多次分配和重新分配只讀欄位。
- 在readonly struct 型別定義中,readonly 指示結構型別是不可變的。
- 在結構型別內的例項成員宣告中,readonly 指示例項成員不修改結構的狀態。
- 在方法返回中,readonly 修飾符指示該方法返回一個引用,且不允許向該引用寫入內容。
C#各種方法
靜態方法
生命週期:一旦建立直到應用程式結束才會消失,全域性。
方法被static修飾,可以直接通過類名.方法名
呼叫
internal class TestMethodClass { static void Main(string[] args) { string a = TestMethodClass.StaticMethod(); Console.Read(); } public static string StaticMethod() { return "This is Static Method"; } }
構造方法
類似於java,預設是有一個無參構造方法,但是不會顯示出來。可以自定義多個有參構造方法。在例項化類的時候(也就是生命週期開始時)會去呼叫構造方法在記憶體中開闢一段空間,儲存這個例項化物件。
方法名與類名一致。
折構方法
宣告折構方法:~類名
一般在GC回收時呼叫,用來釋放物件。一般很少用到。
這裡涉及到託管資源的概念,簡單來講GC機制會自動回收一些資源,但是有一些資源不在GC回收機制範圍內,這部分就是非託管資源,而折構方法一般用在回收非託管資源上面,比如資料庫連結。
C#中IDisposable介面的主要用途是釋放非託管資源。當不再使用託管物件時,垃圾回收器會自動釋放分配給該物件的記憶體。但無法預測進行垃圾回收的時間。另外,垃圾回收器對視窗控制代碼或開啟的檔案和流等非託管資源一無所知。將此介面的Dispose方法與垃圾回收器一起使用來顯式釋放非託管資源。當不再需要物件時,物件的使用者可以呼叫此方法。Dispose()專門回收GC機制回收不了的非託管資源
使用時大概如下:
在一個類中實現IDisposable
Dispose
方法,方法內為回收非託管資源的邏輯,之後宣告折構方法:~類名
,折構方法在物件銷燬時觸發,而在折構方法內部呼叫重寫的Dispose()
方法實現非託管資源的回收。
internal class DisposeTest : IDisposable
{
public void Dispose()
{
throw new NotImplementedException();
}
}
虛/重寫方法
- 虛方法:
virtual
關鍵詞修飾 - 重寫方法:
override
關鍵詞修飾
可被子類重寫該方法,重寫時裡面可以通過base.虛方法名
來呼叫原方法內的邏輯。也可以修改虛方法內容改為新的邏輯,即便修改後也不會更改原虛方法內的方法邏輯。
感覺類似於一個可以實現方法邏輯的抽象方法,可以被其他子類重寫,但自定義的虛方法內部可以自定義功能。
public class VirtualMethod
{
public virtual int Calc(int a, int b)
{
return a + b;
}
}
public class VirtualMethodChild : VirtualMethod {
public override int Calc(int a, int b)
{
return base.Calc(a, b);
}
}
抽象方法
abstract關鍵詞修飾,抽象方法必須寫到抽象類裡面。一般由抽象類定義抽象方法的規範(入參/返回值)之後子類實現抽象方法,完成方法內部具體邏輯。
擴充套件方法
使用場景,當一個類設定了sealed修飾符,那這個類就是密封的,不可以被繼承,也就不可以通過繼承的方式重寫這個類中的方法然後去呼叫了。
- 呼叫密封類的物件、屬性、方法等(擴充套件密封類)
- 擴充套件介面
0x01 擴充套件方法
比如這裡拿sealed
修飾了一個類,但是想呼叫這個裡面的getString
方法
internal sealed class Class4
{
public string getString() {
return "string";
}
}
定義擴充套件方法:靜態類+靜態方法+this 引用型別
呼叫時直接物件名.方法名即可呼叫
static class Program
{
public static void testExtendMethod(this ExtendClass extclass) {
extclass.getString();
}
}
0x02 擴充套件介面
介面
internal interface Interface1
{
int Add(int a, int b);
}
定義擴充套件方法,同時新增了一個方法,這樣當有一個新的類繼承原來的介面Interface1
時也可以呼叫到新增的擴充套件方法ExtendAdd2
public static class ExtendClassTest
{
public static int ExtendAdd1(this Interface1 in1, int a, int b) {
return a + b;
}
public static int ExtendAdd2(this Interface1 in1, int a, int b)
{
return a - b;
}
}
泛型
允許延遲編寫類或方法中的程式設計元素的資料型別的規範,直到實際在程式中使用它的時候。換句話說,泛型允許您編寫一個可以與任何資料型別一起工作的類或方法。
比如設定陣列,用object
賦值的時候 元素可以為int 、string等其他型別。和java泛型差不多。再比如下面的使用泛型類,在類名後跟<T>
,佔位符不一定是T
其他的也可以,但是預設是寫成T
。可以等到真正使用的時候再去設定這個類宣告的一個型別。再有這裡泛型不一定只是定義一個,也可以定義多個。
public class MyGenericArray<T>
{
private T[] array;
public MyGenericArray(int size)
{
array = new T[size + 1];
}
public T getItem(int index)
{
return array[index];
}
public void setItem(int index, T value)
{
array[index] = value;
}
}
class Tester
{
static void Main(string[] args)
{
// 宣告一個整型陣列
MyGenericArray<int> intArray = new MyGenericArray<int>(5);
// 設定值
for (int c = 0; c < 5; c++)
{
intArray.setItem(c, c*5);
}
// 獲取值
for (int c = 0; c < 5; c++)
{
Console.Write(intArray.getItem(c) + " ");
}
Console.WriteLine();
// 宣告一個字元陣列
MyGenericArray<char> charArray = new MyGenericArray<char>(5);
// 設定值
for (int c = 0; c < 5; c++)
{
charArray.setItem(c, (char)(c+97));
}
// 獲取值
for (int c = 0; c < 5; c++)
{
Console.Write(charArray.getItem(c) + " ");
}
Console.WriteLine();
Console.ReadKey();
}
}
泛型方法
泛型方法,在方法名後跟<T>
class Program
{
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a, b;
char c, d;
a = 10;
b = 20;
c = 'I';
d = 'V';
// 在交換之前顯示值
Console.WriteLine("Int values before calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values before calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
// 呼叫 swap
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
// 在交換之後顯示值
Console.WriteLine("Int values after calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values after calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
Console.ReadKey();
}
}
泛型約束
這裡直接貼圖,簡單瞭解,後面遇到了再深入跟一下。
泛型約束,通過在引數宣告之後,通過where關鍵詞指定傳入的泛型的型別約束,比如這裡where T:new()
就表示傳來的泛型T必須是一個類,可以通過new去例項化。
常用的約束如下,介面約束可以有多個
協變和逆變
out/in 修飾符
out修飾的引數只能當作返回值用,不可以被用做內部的具體方法的入參。
in修飾的只能當作引數,不能當作返回值。
委託
C# 中的委託(Delegate)類似於 C 或 C++ 中函式的指標。委託(Delegate) 是存有對某個方法的引用的一種引用型別變數。引用可在執行時被改變。
委託(Delegate)特別用於實現事件和回撥方法。所有的委託(Delegate)都派生自 System.Delegate 類。
委託宣告
宣告格式如下
delegate <return type> <delegate-name> <parameter list>
上面的委託可被用於引用任何一個帶有一個單一的 string 引數的方法,並返回一個 int 型別變數。
這裡感覺就是在內部回去匹配一個入引數量為1,型別為string,且返回值型別為int的方法,並用MyDelegate作為該方法的引用。
public delegate int MyDelegate (string s);
委託例項化
個人感覺類似於一個代理機制,例項化DelegateTest
時傳入相應的(返回值型別,入引數量,入參型別)方法,即可通過DelegateTest
的例項化物件呼叫該方法(感覺是建立了一個對於該方法的引用)
delegate string DelegateTest (string str);
namespace Reflection
{
public class MyReflection
{
public static string Base = "base";
public static string Hello(string str) {
Base += "Hello " + str;
return Base;
}
public static string World(string str) {
Base += "World " + str;
return Base;
}
static void Main(string[] args)
{
DelegateTest delegateTest1 = new DelegateTest(Hello);
DelegateTest delegateTest2 = new DelegateTest(World);
Console.WriteLine(Base);
delegateTest1("delegateTest1");
Console.WriteLine(Base);
delegateTest2("delegateTest2");
Console.WriteLine(Base);
Console.ReadKey();
}
output:
base
baseHello delegateTest1
baseHello delegateTest1World delegateTest2
委託多播
委託物件可使用 "+" 運算子進行合併。一個合併委託呼叫它所合併的兩個委託。只有相同型別的委託可被合併。"-" 運算子可用於從合併的委託中移除元件委託。
執行的話應該是也有順序的,按照+
前後的順序來執行。
delegate string DelegateTest (string str);
namespace Reflection
{
public class MyReflection
{
public static string Base = "base";
public static string Hello(string str) {
Base += "Hello " + str;
return Base;
}
public static string World(string str) {
Base += "World " + str;
return Base;
}
static void Main(string[] args)
{
DelegateTest delegateTest1 = new DelegateTest(Hello);
DelegateTest delegateTest2 = new DelegateTest(World);
DelegateTest dt = delegateTest1 + delegateTest2;
dt("delegateTest");
Console.WriteLine(Base);
output:
baseHello delegateTestWorld delegateTest