国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【一文詳解】知識(shí)分享:(C#開發(fā)學(xué)習(xí)快速入門)

這篇具有很好參考價(jià)值的文章主要介紹了【一文詳解】知識(shí)分享:(C#開發(fā)學(xué)習(xí)快速入門)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

面向?qū)ο?OOP)

c語言是面向過程。

c++是面向過程+面向?qū)ο蟆?/p>

c#是純粹的面向?qū)ο? 核心思想是以人的思維習(xí)慣來分析和解決問題。萬物皆對象。

面向?qū)ο箝_發(fā)步驟:

  1. 分析對象

    • 特征
    • 行為
    • 關(guān)系(對象關(guān)系/類關(guān)系)
  2. 寫代碼:

    特征–>成員變量

    方法–>成員方法

  3. 實(shí)例化–具體對象

Note(補(bǔ)充知識(shí)):

  1. 類=模板

  2. (類我們一般用于定義新的數(shù)據(jù)類型)

    (定義好的類 = 新的數(shù)據(jù)類型,故可以用于定義對應(yīng)類型變量)

  3. 類的成員分為普通成員和靜態(tài)成員

  4. 類間關(guān)系:

    • 泛化(Generalization):
      #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    • 實(shí)現(xiàn)(Realization):

      #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    • 關(guān)聯(lián)(Association):

      #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    • 聚合(Aggregation):

      #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    • 組合(Composition):
      #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    • 依賴(Dependency):

      #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享


.Net(framework)和C#

兩者的關(guān)系:.Net Framework 的地位類似于 Java中的JVM.

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

c#語言的編譯過程:

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

Note:

  1. C#的語言編譯器: csc(c sharp compiler),其與.Net框架安裝在一起,即在同一個(gè)安裝路徑下。

    例如,在我本機(jī)的路徑是:C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe

數(shù)據(jù)類型

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

c# partial關(guān)鍵字

在 C# 中,您可以使用 partial 關(guān)鍵字在多個(gè) .cs 文件中拆分類、結(jié)構(gòu)、方法或接口的實(shí)現(xiàn)。

編譯程序時(shí),編譯器將自動(dòng)合并來自多個(gè) .cs 文件的所有實(shí)現(xiàn)。


部分類:

示例如下:

EmployeeProps.cs

public partial class Employee
{
    public int EmpId { get; set; }
    public string Name { get; set; }
}

EmployeeMethods.cs

public partial class Employee
{
    //constructor
    public Employee(int id, string name)
    {
        this.EmpId = id;
        this.Name = name;
    }

    public void DisplayEmpInfo()
    {
        Console.WriteLine(this.EmpId + " " this.Name);
    }
}

上面,EmployeeProps.cs 包含了 Employee 類的屬性,EmployeeMethods.cs 包含了 Employee 類的所有方法。

這些將被合并編譯為一個(gè) Employee 類。

Employee

public class Employee
{
    public int EmpId { get; set; }
    public string Name { get; set; }

    public Employee(int id, string name)
    {
        this.EmpId = id;
        this.Name = name;
    }

    public void DisplayEmpInfo()
    {
        Console.WriteLine(this.EmpId + " " this.Name );
    }
}

部分類的規(guī)則:

  • 所有部分類定義必須在相同的程序集和命名空間中。
  • 所有部分類必須具有相同的可訪問性,例如公共或私有等。
  • 如果任何部分聲明為抽象、密封或基類型,則整個(gè)類聲明為相同類型。
  • 不同的部分類必須有相同的基類型。
  • Partial 修飾符只能出現(xiàn)在關(guān)鍵字 class、struct 或 interface 之前。
  • 允許嵌套的部分類。

什么時(shí)候會(huì)用到部分類:

通常是一個(gè)類太大,或者有一些類我們是用代碼生成的。

然后我們自己的方法代碼不希望影響到開源軟件生成的代碼 ,即我們不希望影響到舊的代碼。這樣維護(hù)的時(shí)候很麻煩。

部分方法拆分為分部類或結(jié)構(gòu)的兩個(gè)單獨(dú)的 .cs 文件。

兩個(gè) .cs 文件之一包含方法的簽名,另一個(gè)文件可以包含這個(gè)部分方法的實(shí)現(xiàn)。且 方法的聲明和實(shí)現(xiàn)都必須有 partial 關(guān)鍵字。

EmployeeProps.cs

public partial class Employee{
    public Employee(){
        GenerateEmpId();
    }
    
    public Guid EmpId{get;set;}
    public string name{get;set;}
    
    partial void GenerateEmpId();
}

EmployeeMethods.cs

public partial class Employee{
    partial void GenerateEmpId(){
        this.EmpId  = 	Guid.NewGuid();
    }
}

部分方法的規(guī)則:

  • 部分方法必須使用 partial 關(guān)鍵字

  • 部分方法如果不是void的話,則必須加訪問修飾符。

  • 部分方法可以使用 in, ref, out 來修飾參數(shù)。

  • 部分方法是隱式私有方法,因此不能是虛擬的。

不支持virtual

  • 部分方法可以是靜態(tài)方法。

  • 部分方法可以是泛型

c# using關(guān)鍵字

在 C# 中,using 關(guān)鍵字有兩個(gè)主要的用途:

1.資源管理(Resource Management): using 用于在代碼塊執(zhí)行完畢后釋放資源,確保對象在離開作用域時(shí)被正確清理。這通常用于實(shí)現(xiàn) IDisposable 接口的對象,如文件流、數(shù)據(jù)庫連接等。

using (FileStream fs = new FileStream("example.txt", FileMode.Open))
{
    // 使用文件流進(jìn)行操作
    // 在這個(gè)代碼塊結(jié)束時(shí),F(xiàn)ileStream 會(huì)被自動(dòng)關(guān)閉和釋放資源
}

2.導(dǎo)入命名空間(Namespace Import): using 也用于導(dǎo)入命名空間,使得在代碼中可以直接使用命名空間中的類型,而不需要使用完全限定名。提高代碼的可讀性.

//導(dǎo)入命名空間
using NamespaceName;

注意:

using 關(guān)鍵字用于導(dǎo)入命名空間,但它只導(dǎo)入指定的命名空間,不會(huì)自動(dòng)導(dǎo)入該命名空間下的所有子命名空間。

例如,如果你使用了 using System; 導(dǎo)入了 System 命名空間,那么只有 System 命名空間下的類型會(huì)被導(dǎo)入,而 System.ThreadingSystem.IO 等子命名空間下的類型不會(huì)自動(dòng)導(dǎo)入。

如果需要使用子命名空間的類型,你需要額外使用 using 導(dǎo)入相應(yīng)的子命名空間。這樣可以根據(jù)需要有選擇地導(dǎo)入所需的命名空間,而不會(huì)導(dǎo)入整個(gè)命名空間的所有內(nèi)容,從而保持代碼的簡潔性和可維護(hù)性。

c# var和dynamic關(guān)鍵字

var關(guān)鍵字

var 是 C# 中的一個(gè)關(guān)鍵字,用于在聲明變量時(shí)讓編譯器根據(jù)初始化表達(dá)式的類型來自動(dòng)推斷變量的類型。

使用 var 關(guān)鍵字可以使代碼更為簡潔,尤其是在處理復(fù)雜的類型或匿名類型時(shí)。

  1. 基本用法:
var number = 42; // 推斷為 int 類型
var name = "John"; // 推斷為 string 類型
var price = 19.99; // 推斷為 double 類型
  1. 在集合中使用:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
foreach (var num in numbers)
{
    // num 的類型被推斷為 int
    Console.WriteLine(num);
}
  1. 匿名類型(Anonymous Types):
var person = new { Name = "John", Age = 30 };
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
  1. LINQ 查詢中的匿名類型:
var result = from p in people
             where p.Age > 21
             select new { p.Name, p.Age };
  1. 在方法中使用:
var result = Add(3, 5);

int Add(int a, int b)
{
    return a + b;
}
  1. LINQ 方法語法查詢中的 var:
var adults = people.Where(p => p.Age >= 18).ToList();

注意事項(xiàng):

  • var 不是一種動(dòng)態(tài)類型,而是在編譯時(shí)確定類型的隱式類型。

  • 在聲明變量時(shí)使用 var 時(shí),必須在同一行進(jìn)行初始化。

    var i = 10;
    

    否則 C# 編譯器會(huì)報(bào)錯(cuò):必須初始化隱式類型的變量。

    var i; // 編譯錯(cuò)誤: 隱式類型化的變量必須已初始化 Implicitly-typed variables must be initialized
    i = 100;
    
  • 使用 var 時(shí),編譯器會(huì)根據(jù)初始化表達(dá)式的類型進(jìn)行類型推斷。

  • 不允許使用一個(gè)var 定義多個(gè)變量

    var i = 100, j = 200, k = 300; // Error: 隱式類型化的變量不能有多個(gè)聲明符
    
  • var不能用在方法的形參參數(shù)上面

    void Display(var param) //編譯錯(cuò)誤
    {
        Console.Write(param);
    }
    
  • var 可以用在for 和 foreach的循環(huán)當(dāng)中

    for(var i = 0;i < 10;i++){
        Console.WriteLine(i);
    }
    
  • var 還可以被用在 linq的查詢當(dāng)中

    特別是在linq查詢當(dāng)中如果返回類型是匿名類型的時(shí)候,也只能用var了。

    var stringList = new List<string>(){
        "C# 教程",
        "VB.NET 教程",
        "Learn C++",
        "MVC 教程" ,
        "Java" 
    };
    
    //Linq查詢語法,(跟sql很像)
    var result = from s in stringList
        		where s.Contains("教程")
        		select s;
    

dynamic關(guān)鍵字

在C#中,dynamic 是一種在運(yùn)行時(shí)執(zhí)行類型檢查的類型。使用 dynamic 聲明的變量可以在運(yùn)行時(shí)改變其類型,而不是在編譯時(shí)確定。這種靈活性使得 dynamic 在處理與運(yùn)行時(shí)相關(guān)的場景時(shí)非常有用。

  1. 基本用法:
dynamic dynamicVariable = 10;
Console.WriteLine(dynamicVariable); // 輸出 10

dynamicVariable = "Hello";
Console.WriteLine(dynamicVariable); // 輸出 Hello

在這個(gè)例子中,dynamicVariable 可以在運(yùn)行時(shí)切換為不同的類型。

  1. 與反射結(jié)合使用:
Assembly assembly = Assembly.LoadFile("Example.dll");
dynamic instance = assembly.CreateInstance("Example.MyClass");

// 調(diào)用 MyClass 中的方法
instance.MyMethod();

dynamic 可以與反射結(jié)合使用,使得在運(yùn)行時(shí)訪問和調(diào)用類型的成員更為方便。

  1. 作為方法的參數(shù)或返回類型:
public dynamic GetDynamicResult()
{
    return DateTime.Now.Second % 2 == 0 ? 42 : "Hello";
}

dynamic result = GetDynamicResult();
Console.WriteLine(result); // 輸出 42 或 Hello

dynamic 可以作為方法的參數(shù)或返回類型,使得方法的行為在運(yùn)行時(shí)更加靈活。

需要注意的是,dynamic 是一種犧牲了編譯時(shí)類型檢查的靈活性,因此在使用時(shí)需要小心,并確保在運(yùn)行時(shí)不會(huì)導(dǎo)致類型錯(cuò)誤。

在大多數(shù)情況下,應(yīng)盡量使用靜態(tài)類型以便在編譯時(shí)發(fā)現(xiàn)潛在的錯(cuò)誤。


var和dynamic的對比

對于var關(guān)鍵字:

  • 編譯時(shí)類型推斷: var 用于在編譯時(shí)根據(jù)初始化表達(dá)式的類型推斷變量的類型。

  • 靜態(tài)類型: 一旦類型確定,變量就成為該類型的靜態(tài)類型,后續(xù)不能更改。

  • 局部變量: var 通常用于聲明局部變量,如方法內(nèi)的變量。

  • 不能用于聲明成員變量: var 不能用于聲明類的成員變量,因?yàn)関ar需要初始化表達(dá)式,而類的成員變量不會(huì)初始化表達(dá)式。

對于dynamic關(guān)鍵字:

  • 運(yùn)行時(shí)類型推斷: dynamic 用于在運(yùn)行時(shí)推斷變量的類型。類型檢查是在運(yùn)行時(shí)而不是在編譯時(shí)進(jìn)行的。
  • 動(dòng)態(tài)類型: dynamic 聲明的變量是動(dòng)態(tài)類型的,可以在運(yùn)行時(shí)更改其類型。
  • 可用于聲明成員變量: dynamic 可以用于聲明類的成員變量,因?yàn)樗灰笤诼暶鲿r(shí)就指定類型和初始化表達(dá)式。

c# new關(guān)鍵字

c#中的new關(guān)鍵字作用:

  1. 要么可以在實(shí)例化一個(gè)對象時(shí)使用,如下示例:

    int[] numbers = new int[5];// 使用 new 初始化整數(shù)數(shù)組,默認(rèn)值為 0 
    
  2. 要么可以隱藏基類(父類)成員時(shí)使用:

    在 C# 中,new 關(guān)鍵字用于明確表示你正在故意隱藏父類的成員。

    不加 new 也可以隱藏父類的成員,但會(huì)產(chǎn)生編譯器警告,提醒你可能是無意中隱藏了父類的成員。

    class Parent
    {
        public void Display()
        {
            Console.WriteLine("Parent Display");
        }
    }
    
    class Child : Parent
    {
        //不加new,編譯器會(huì)發(fā)出警告,因?yàn)?Child 類中的 Display 方法沒有使用 new 或 override 關(guān)鍵字,可能會(huì)無意隱藏了父類的成員。然而,程序仍然可以編譯和運(yùn)行。
        public void Display()
        {
            Console.WriteLine("Child Display");
        }
    }
    
    ----------------------------------------------------
    class Parent
    {
        public void Display()
        {
            Console.WriteLine("Parent Display");
        }
    }
    
    class Child : Parent
    {
        //使用了new
        //在這種情況下,使用了 new 關(guān)鍵字,明確表示是有意隱藏父類的成員。
        //編譯器將不再發(fā)出警告,并且程序可以正常編譯和運(yùn)行。
        public new void Display()
        {
            Console.WriteLine("Child Display");
        }
    }
    

c# out關(guān)鍵字

使用 out 關(guān)鍵字的主要原因是為了允許方法返回多個(gè)值。在 C# 中,方法只能返回一個(gè)值。

但是,如果你需要從方法中返回多個(gè)值,你可以將其中一個(gè)或多個(gè)值作為 out 參數(shù)傳遞給該方法。

總之,out 關(guān)鍵字的作用是允許方法返回多個(gè)值,因?yàn)樗试S在方法內(nèi)部修改參數(shù)的值,并在方法完成后將修改后的值傳遞回調(diào)用方。

以下是一個(gè)簡單的示例,展示了如何使用 out 關(guān)鍵字:

public void Calculate(int input, out int output)
{
    // 在這里進(jìn)行計(jì)算
    output = 42; // 將計(jì)算結(jié)果保存在輸出參數(shù)中
}

// 調(diào)用 Calculate 方法
int result;
Calculate(10, out result);
Console.WriteLine(result); // 輸出 42

//在上面的示例中,Calculate 方法接受一個(gè)整數(shù)作為輸入,并將計(jì)算結(jié)果保存在 output 參數(shù)中。
//在調(diào)用該方法時(shí),我們將一個(gè)變量作為 out 參數(shù)傳遞給該方法,以便在方法完成后獲取計(jì)算結(jié)果。

我的理解: 使用out關(guān)鍵字后,傳遞進(jìn)去的實(shí)參經(jīng)過方法內(nèi)部修改后也會(huì)更新保存到這個(gè)實(shí)參中。

//這個(gè)例子中,CalculateSumAndDifference 方法接受兩個(gè)整數(shù) a 和 b,并使用兩個(gè) out 參數(shù) sum 和 difference 返回它們的和和差。在調(diào)用這個(gè)方法時(shí),傳遞給方法的 sum 和 difference 參數(shù)實(shí)際上是用于接收輸出的變量。

//通過這種方式,你可以使用 out 參數(shù)在一個(gè)方法中返回多個(gè)值。
//注意,out 參數(shù)在調(diào)用方法前不需要初始化,因?yàn)榉椒〞?huì)在內(nèi)部為這些參數(shù)賦值。
class Program
{
    static void Main()
    {
        int inputA = 5;
        int inputB = 3;
        int sum;
        int difference;
		//調(diào)用時(shí)方法要傳遞out
        CalculateSumAndDifference(inputA, inputB, out sum, out difference);

        Console.WriteLine($"Sum: {sum}, Difference: {difference}");
    }
	//聲明時(shí)方法也要傳遞out
    static void CalculateSumAndDifference(int a, int b, out int sum, out int difference)
    {
        sum = a + b;
        difference = a - b;
    }
}

屬性

  • c#中,類中的屬性和方法, 若沒有定義public等權(quán)限修飾符時(shí),默認(rèn)是private,只能在類內(nèi)進(jìn)行訪問。
  • 若要跨類訪問,可以修改為public的權(quán)限修飾符。如: public float size;但直接修改為public屬性的缺點(diǎn)是外部的類可以任意修改一個(gè)類的屬性值,所以遇到這種情況,在Java中的解決方案是外部類利用類內(nèi)的getter和setter方法去訪問和修改類的成員變量,同時(shí)類內(nèi)的getter和setter方法也可在方法中進(jìn)行一定的邏輯處理和限制,從而達(dá)到不讓外部類隨便改類內(nèi)字段值的目的。
  • C#中,則是利用訪問器/屬性解決這個(gè)問題,其思想也類似于Java的POJO類中的getter和setter。

常規(guī)屬性

什么是常規(guī)屬性?

答: 在類中,為了安全起見,一般把數(shù)據(jù)成員設(shè)置為private

即先定義一個(gè)private的私有的字段,然后再為這個(gè)私有字段封裝一個(gè)public公開的屬性,在屬性中實(shí)現(xiàn) get 和 set 兩個(gè)方法,這種方式叫做常規(guī)屬性

注意: private權(quán)限的變量命名一般首字母小寫。

? 而public權(quán)限的變量命令一般首字母大寫。

常規(guī)屬性有什么作用?

答:C#中常規(guī)屬性用于對類中成員變量的取值或賦值進(jìn)行限制過濾。

示例如下:

class demo{
    //先定義一個(gè)私有的字段
    private float size;
    //然后再為這個(gè)私有字段封裝一個(gè)公開的屬性,在屬性中實(shí)現(xiàn) get 和 set 兩個(gè)方法
    //常規(guī)屬性用于對類中成員變量的取值或賦值進(jìn)行限制過濾。**
    public float Size{
        get{return this.size};
        set{this.size = value};
    }
}

//上面的示例中,我們將`size`這個(gè)字段的權(quán)限修飾符由`public`改為`private`,同時(shí)定義了一個(gè)屬性代碼`public float Size{}`,里面 的`get{}`代碼段用于返回屬性值,其中的`set{}`代碼段用于設(shè)置值,且其中的`value`是保留字,其代表了外界賦給這個(gè)字段的值

自動(dòng)屬性

什么情況下使用自動(dòng)屬性?

在某些情況下,屬性的 get 和 set 只是完成字段的取值和賦值操作,而不包含任何附加的邏輯代碼,這個(gè)時(shí)候可以使用自動(dòng)屬性。

什么是自動(dòng)屬性?

答: 是微軟提供的一種快速簡潔建立屬性的方式??偟膩碚f,是一種相比常規(guī)屬性而言更加簡潔的寫法。

如:public int Age { get; set; }

其相當(dāng)于get{}代碼段里面默認(rèn)寫好了return某個(gè)字段,set{}代碼段里面默認(rèn)寫好了this.xx字段=value。

同時(shí),我們使用自動(dòng)屬性的時(shí)候,就不需要再寫對應(yīng)的私有字段了,C#編譯器會(huì)自動(dòng)給我們的自動(dòng)屬性提供一個(gè)對應(yīng)的字段,即不用手工聲明一個(gè)私有的字段。

注意:

  1. 如果是想只能只讀/只能只寫,自動(dòng)屬性代碼如何寫?如下:

    加個(gè)private關(guān)鍵字從而達(dá)到限制的目的。

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  2. 自動(dòng)屬性同時(shí)實(shí)現(xiàn)get和set訪問器,不能使用自動(dòng)屬性只實(shí)現(xiàn)其中的一種,

    public String Name { get; }這種寫法會(huì)報(bào)錯(cuò)的。

  3. 在vs IDE中輸入prop ,點(diǎn)擊兩次tab會(huì)生成一個(gè)默認(rèn)的自動(dòng)屬性

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

兩種屬性對比

對比常規(guī)屬性自動(dòng)屬性:

  • 自動(dòng)屬性寫起來的更快,且不用自己寫get,set方法,直接使用默認(rèn)的;

  • 兩者來說,可能自動(dòng)屬性對比常規(guī)屬性,可能會(huì)少些兩句代碼,但不如常規(guī)屬性自己寫的方法靈活,按需使用。


字段和屬性的異同

  1. 訪問方式:

    • 字段(Field): 是類中的變量,直接存儲(chǔ)數(shù)據(jù)。

      可以通過類的實(shí)例直接訪問,但一般來說不鼓勵(lì)直接暴露字段,因?yàn)檫@會(huì)破壞封裝性。

      class MyClass
      {
          public int myField; // 字段
      }
      
    • 屬性(Property): 提供了一種更加控制和靈活的方式來訪問或修改類的私有字段的值。

      屬性具有讀取器(getter)和寫入器(setter),可以在讀取和寫入數(shù)據(jù)時(shí)執(zhí)行自定義邏輯。

      class MyClass
      {
          private int myField; // 私有字段
      
          public int MyProperty // 屬性
          {
              get { return myField; }
              set { myField = value; }
          }
      }
      
  2. 封裝性:

    • 字段(Field): 通常是類的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),可以通過直接暴露為 public 或 internal 字段來使其對外可見,但這樣做會(huì)破壞封裝性。
    • 屬性(Property): 提供了更好的封裝性,允許類作者在保護(hù)類內(nèi)部實(shí)現(xiàn)的同時(shí),對外提供更安全和靈活的訪問接口。
  3. 使用場景:

    • 字段(Field): 適用于不需要添加邏輯的簡單數(shù)據(jù)存儲(chǔ),或者在某些情況下需要對字段進(jìn)行直接訪問的場景。
    • 屬性(Property): 適用于需要在讀取或?qū)懭霐?shù)據(jù)時(shí)執(zhí)行一些邏輯、驗(yàn)證或計(jì)算的場景。它提供了更靈活的控制和更好的封裝性。

在實(shí)際的編程中,一般建議使用屬性而不是直接暴露字段,以便更好地控制對類內(nèi)部實(shí)現(xiàn)的訪問。

C# 中的自動(dòng)屬性(Auto-implemented Properties)提供了一種更簡潔的語法來聲明屬性,省去了手動(dòng)實(shí)現(xiàn) get 和 set 方法的步驟。

class MyClass
{
    public int MyProperty { get; set; } // 自動(dòng)屬性
}
//在這個(gè)例子中,`MyProperty` 是一個(gè)公共的自動(dòng)屬性,編譯器會(huì)`自動(dòng)生成相應(yīng)的私有字段以及默認(rèn)的 get 和 set 方法`。

委托

what

  • 在C#中,委托是一種能夠存儲(chǔ)對方法的引用的類型。

    它允許您將方法作為參數(shù)傳遞給其他方法將方法存儲(chǔ)在變量中,委托可以被認(rèn)為是函數(shù)指針。

  • 通過委托,你可以像傳遞變量一樣傳遞方法, 它可以使你像傳遞參數(shù)一樣傳遞方法。

  • 主要用于實(shí)現(xiàn)回調(diào)機(jī)制、事件處理和委托鏈等。

委托定義了一個(gè)方法簽名,它指定了可以由該委托引用的方法的返回類型和參數(shù)列表

委托定義的方法簽名定義了它所能代表的方法種類。

可以將任何符合該方法簽名的方法分配給該委托,使其成為它的實(shí)例。

一旦將方法分配給委托,您可以像調(diào)用方法一樣調(diào)用該委托,以便在調(diào)用委托時(shí)執(zhí)行該方法。

使用委托的作用:

1、避免核心方法中存在大量的if…else…語句(或swich開關(guān)語句);

2、滿足程序設(shè)計(jì)的OCP原則;

3、使程序具有擴(kuò)展性;

4、綁定事件;

5、結(jié)合Lambda表達(dá)式,簡化代碼,高效編程;

6、實(shí)現(xiàn)程序的松耦合(解耦),這個(gè)在事件(event)中體現(xiàn)比較明顯;


**委托是一個(gè)類,它定義了方法的類型,使得可以將方法當(dāng)作另一個(gè)方法的參數(shù)來進(jìn)行傳遞,這種將方法動(dòng)態(tài)地賦給參數(shù)的做法,可以避免在程序中大量使用If-Else(Switch)**語句,同時(shí)使得程序具有更好的可擴(kuò)展性。


委托是對函數(shù)的引用,如果我們調(diào)用委托,實(shí)際是調(diào)用的委托引用的函數(shù)。委托擁有一個(gè)函數(shù)或一組函數(shù)的所有必要信息,包括簽名和返回值類型。


靜態(tài)設(shè)計(jì)時(shí),當(dāng)我們不知道具體哪個(gè)函數(shù)會(huì)被調(diào)用時(shí),我們可以使用委托。

定義

delegate關(guān)鍵字定義委托,定義委托,它定義了可以代表的方法的類型。

(注意,委托是沒有方法體的,類似接口里面的方法)

在定義委托前,必須明確兩個(gè)問題:

1、委托將要綁定的方法;

2、委托的形參類型,形參個(gè)數(shù)和委托的返回值必須與將要綁定的方法的形參類型,形參個(gè)數(shù)和返回值一致

public delegate 委托返回類型 委托名(形參)


// 定義委托
delegate string MyDelegate(string message);


調(diào)用

可以使用 Invoke() 方法或使用 () 運(yùn)算符調(diào)用委托。

如下示例:

public delegate void MyDelegate(string message);//聲明一個(gè)委托

class Program{
    // 跟MyDelegate有相同參數(shù)和返回值的方法 稱為簽名相同
    static void MethodA(string message)
    {
        Console.WriteLine(message);
    }
    
    static void Main(string[] args){
        //把函數(shù)傳給委托
        MyDelegate md = new MyDelegate(MethodA);
        //或者直接賦值給委托
        MyDelegate md2 = MethodA;
        //使用Lambda表達(dá)式
        MyDelegate  md3 = (string message) => Console.WriteLine(message);
        
        //設(shè)置目標(biāo)方法后,可以使用 Invoke() 方法或使用 () 運(yùn)算符調(diào)用委托。
        md.Invoke("hello");
        md2("world");
        md3("hello world");
    }
}

操作/種類

注意:

在 C# 中,如果一個(gè)委托變量沒有綁定任何具體的方法,它的值將為 null。

1、單播委托綁定方法:綁定單個(gè)方法

  • 直接傳遞方法名稱綁定對應(yīng)的方法和調(diào)用委托,如下:
//定義 委托
public delegate void GreetingDelegate(string name);

//具體方法1
private static void EnglishGreeting(string name) {
    Console.WriteLine("Morning, " + name);
}
//具體方法2
private static void ChineseGreeting(string name) {
    Console.WriteLine("早上好, " + name);
}

 //調(diào)用委托的方法,注意此方法,它接受一個(gè)GreetingDelegate類型的方法作為參數(shù)
private static void GreetPeople(string name, GreetingDelegate MakeGreeting) {
    MakeGreeting(name);
}

//直接傳遞方法名稱調(diào)用委托(如同調(diào)用方法)
GreetPeople("Jimmy Zhang", EnglishGreeting);
GreetPeople("張子陽", ChineseGreeting);
  • 先聲明委托變量,再使用=給委托變量賦值具體方法

    最后傳遞委托變量來調(diào)用委托, 如下:

//定義 委托
public delegate void GreetingDelegate(string name);

//具體方法
private static void EnglishGreeting(string name) {
    Console.WriteLine("Morning, " + name);
}
//具體方法
private static void ChineseGreeting(string name) {
    Console.WriteLine("早上好, " + name);
}

 //注意此方法,它接受一個(gè)GreetingDelegate類型的方法作為參數(shù)
private static void GreetPeople(string name, GreetingDelegate MakeGreeting) {
    MakeGreeting(name);
}

//聲明委托變量
GreetingDelegate delegate1, delegate2;
//委托變量賦值:委托變量綁定具體方法
delegate1 = EnglishGreeting;
delegate2 = ChineseGreeting;

//調(diào)用委托(如同調(diào)用方法)
GreetPeople("Jimmy Zhang", delegate1);
GreetPeople("張子陽", delegate2);
  • 也可以繞過GreetPeople方法,通過委托來直接調(diào)用EnglishGreeting和ChineseGreeting:
//定義 委托
public delegate void GreetingDelegate(string name);

//具體方法
private static void EnglishGreeting(string name) {
    Console.WriteLine("Morning, " + name);
}
//具體方法
private static void ChineseGreeting(string name) {
    Console.WriteLine("早上好, " + name);
}

//聲明委托變量
GreetingDelegate delegate1;

//委托變量賦值
delegate1 = EnglishGreeting; // 先給委托類型的變量賦值
delegate1 += ChineseGreeting;   // 給此委托變量再綁定一個(gè)方法

//調(diào)用委托
//將先后調(diào)用 EnglishGreeting 與 ChineseGreeting 方法
delegate1 ("Jimmy Zhang");  

2、多播委托綁定方法:利用+=綁定多個(gè)方法

可以將多個(gè)方法賦給同一個(gè)委托,或者叫將多個(gè)方法綁定到同一個(gè)委托,當(dāng)調(diào)用這個(gè)委托的時(shí)候,將依次調(diào)用其所綁定的方法。

注意:

  • 綁定多個(gè)方法時(shí),委托范圍類型必須為void類型,否則只返回最后一個(gè)綁定的值。
  • 使用委托可以將多個(gè)方法綁定到同一個(gè)委托變量,當(dāng)調(diào)用此變量時(shí)(這里用“調(diào)用”這個(gè)詞,是因?yàn)榇俗兞看硪粋€(gè)方法),可以依次調(diào)用所有綁定的方法。

  • 可利用第一個(gè)方法用=形式,其余方法用+= 的形式來進(jìn)行多播委托,如下:

注意這里,第一次用的“=”,是賦值的語法;第二次,用的是“+=”,是綁定的語法。
如果第一次就使用“+=”,將出現(xiàn)“使用了未賦值的局部變量”的編譯錯(cuò)誤。

//定義 委托
public delegate void GreetingDelegate(string name);

//具體方法
private static void EnglishGreeting(string name) {
    Console.WriteLine("Morning, " + name);
}
//具體方法
private static void ChineseGreeting(string name) {
    Console.WriteLine("早上好, " + name);
}

//調(diào)用委托: 注意此方法,它接受一個(gè)GreetingDelegate類型的方法作為參數(shù)
private static void GreetPeople(string name, GreetingDelegate MakeGreeting) {
    MakeGreeting(name);
}

//聲明委托變量
GreetingDelegate delegate1;


//委托變量綁定具體方法
//注意這里,第一次用的“=”,是賦值的語法;第二次,用的是“+=”,是綁定的語法。
//如果第一次就使用“+=”,將出現(xiàn)“使用了未賦值的局部變量”的編譯錯(cuò)誤。
delegate1 = EnglishGreeting;// 先用=給委托類型的變量賦值一個(gè)方法
delegate1 += ChineseGreeting;// 再用+=給此委托變量再綁定一個(gè)方法

//調(diào)用委托(如同調(diào)用方法)
//將先后調(diào)用 EnglishGreeting 與 ChineseGreeting 方法
GreetPeople("Jimmy Zhang",delegate1);
  • 也可以使用下面的代碼來這樣簡化其中的多播委托過程:
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);//先利用類似構(gòu)造函new委托變量形式傳遞綁定一個(gè)方法
delegate1 += ChineseGreeting;  // 給此委托變量再綁定一個(gè)方法
-----------------------------------------------------------------------------------------------
你不禁想到:上面第一次綁定委托時(shí)不可以使用“+=”的編譯錯(cuò)誤,或許可以用這樣的方法來避免:

GreetingDelegate delegate1 = newGreetingDelegate();
delegate1 += EnglishGreeting;   // 這次用的是 “+=”,綁定語法。
delegate1 += ChineseGreeting;   // 給此委托變量再綁定一個(gè)方法

但實(shí)際上,這樣會(huì)出現(xiàn)編譯錯(cuò)誤: 因?yàn)椤癎reetingDelegate”所代表方法沒有采用“0”個(gè)參數(shù)的重載。有的話還可以嘗試下。

所以之后總的代碼就變成,如下:

//定義 委托
public delegate void GreetingDelegate(string name);

//具體方法
private static void EnglishGreeting(string name) {
    Console.WriteLine("Morning, " + name);
}
//具體方法
private static void ChineseGreeting(string name) {
    Console.WriteLine("早上好, " + name);
}

//調(diào)用委托: 注意此方法,它接受一個(gè)GreetingDelegate類型的方法作為參數(shù)
private static void GreetPeople(string name, GreetingDelegate MakeGreeting) {
    MakeGreeting(name);
}


//先利用類似構(gòu)造函new委托變量形式傳遞綁定一個(gè)方法
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
//給此委托變量再綁定一個(gè)方法
delegate1 += ChineseGreeting;  

//調(diào)用委托(如同調(diào)用方法)
//將先后調(diào)用 EnglishGreeting 與 ChineseGreeting 方法
GreetPeople("Jimmy Zhang",delegate1);

3、解綁方法:利用-=解綁方法

//定義 委托
public delegate void GreetingDelegate(string name);

//具體方法1
private static void EnglishGreeting(string name) {
    Console.WriteLine("Morning, " + name);
}
//具體方法2
private static void ChineseGreeting(string name) {
    Console.WriteLine("早上好, " + name);
}

//調(diào)用委托: 注意此方法,它接受一個(gè)GreetingDelegate類型的方法作為參數(shù)
private static void GreetPeople(string name, GreetingDelegate MakeGreeting) {
    MakeGreeting(name);
}


//先利用類似構(gòu)造函new委托變量形式傳遞綁定一個(gè)方法
GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
//給此委托變量再綁定一個(gè)方法
delegate1 += ChineseGreeting;  

//調(diào)用委托(如同調(diào)用方法)
//將先后調(diào)用 EnglishGreeting 與 ChineseGreeting 方法
GreetPeople("Jimmy Zhang",delegate1);


//利用-=解綁方法
delegate1 -= EnglishGreeting; //取消對EnglishGreeting方法的綁定
// 將僅調(diào)用 ChineseGreeting
GreetPeople("張子陽", delegate1);

普通委托

示例demo1.cs:

namespace test{
     //定義委托,它定義了可以代表的方法的類型
    public delegate void GreetingDelegate(string name);
    
   	//新建的GreetingManager類
    public class GreetingManager{
        //包含調(diào)用委托的方法
       public void GreetPeople(string name, GreetingDelegate MakeGreeting) {
           MakeGreeting(name);//調(diào)用委托
       }
    }
    
    class Program{
        //具體方法1
        private static void EnglishGreeting(string name) {
            Console.WriteLine("Morning, " + name);
        }
        //具體方法2
        private static void ChineseGreeting(string name) {
            Console.WriteLine("早上好, " + name);
        }
        
        //Main
        static void Main(string[] args){
            //創(chuàng)建類的實(shí)例對象
            GreetingManager greetingManager = new GreetingManager();
            //給調(diào)用委托的方法傳入具體的方法
            greetingManager.GreetPeople("Jimmy Zhang",EnglishGreeting);
            greetingManager.GreetPeople("章三",ChineseGreeting);
        }
    }
    
}

現(xiàn)在,假設(shè)需要將多個(gè)具體方法綁定到同一個(gè)委托變量,該如何做呢?再次改寫代碼,如下:

namespace test{
     //定義委托,它定義了可以代表的方法的類型
    public delegate void GreetingDelegate(string name);
    
   	//新建的GreetingManager類
    public class GreetingManager{
        //包含調(diào)用委托的方法
       public void GreetPeople(string name, GreetingDelegate MakeGreeting) {
           MakeGreeting(name);//調(diào)用委托
       }
    }
    
    class Program{
        //具體方法1
        private static void EnglishGreeting(string name) {
            Console.WriteLine("Morning, " + name);
        }
        //具體方法2
        private static void ChineseGreeting(string name) {
            Console.WriteLine("早上好, " + name);
        }
        
        //Main(客戶端)
        static void Main(string[] args){
            //創(chuàng)建類的實(shí)例對象
            GreetingManager greetingManager = new GreetingManager();
            //聲明委托變量
            GreetingDelegate delegate1;
            //給委托變量進(jìn)行多播委托綁定方法
            delegate1 = EnglishGreeting;
            delegate1 += ChineseGreeting;
            //調(diào)用委托(相當(dāng)于調(diào)用方法)
            greetingManager.GreetPeople("Jimmy Zhang",delegate1); 
        }
    }   
}

既然可以聲明委托類型的變量(在上例中是delegate1),為何不將這個(gè)變量封裝到 GreetManager類中?

這樣客戶端就不用去手動(dòng)聲明委托變量了,改寫代碼后的結(jié)果如下:

namespace test{
     //定義委托,它定義了可以代表的方法的類型
    public delegate void GreetingDelegate(string name);
    
   	//新建的GreetingManager類
    public class GreetingManager{
        //聲明委托變量
        public GreetingDelegate delegate1;
        //包含調(diào)用委托的方法
       public void GreetPeople(string name, GreetingDelegate MakeGreeting) {
           MakeGreeting(name);//調(diào)用委托
       }
    }
    
    class Program{
        //具體方法1
        private static void EnglishGreeting(string name) {
            Console.WriteLine("Morning, " + name);
        }
        //具體方法2
        private static void ChineseGreeting(string name) {
            Console.WriteLine("早上好, " + name);
        }
        
        //Main(客戶端)
        static void Main(string[] args){
            //創(chuàng)建類的實(shí)例對象
            GreetingManager greetingManager = new GreetingManager();
            //給類中的委托變量屬性進(jìn)行多播委托綁定方法
            greetingManager.delegate1 = EnglishGreeting;
            greetingManager.delegate1 += ChineseGreeting;
            //調(diào)用委托(相當(dāng)于調(diào)用方法)
            greetingManager.GreetPeople("Jimmy Zhang",greetingManager.delegate1); 
        }
    }   
}

盡管這樣做沒有任何問題,但我們發(fā)現(xiàn)這條語句寫起來很奇怪。

即在調(diào)用greetingManager.GreetPeople方法的時(shí)候,傳遞了greetingManager的delegate1字段。

既然如此,我們修改 GreetingManager 類成這樣:

//新建的GreetingManager類
public class GreetingManager{
    //聲明委托變量
    public GreetingDelegate delegate1;
    //包含調(diào)用委托的方法
    public void GreetPeople(string name) {
        if(delegate1!=null){     //如果有方法注冊委托變量
            delegate1(name);      //通過委托調(diào)用方法
        }
    }

然后修改后的最終代碼如下:

namespace test{
     //定義委托,它定義了可以代表的方法的類型
    public delegate void GreetingDelegate(string name);
    
    //新建的GreetingManager類
    public class GreetingManager{
        //聲明委托變量
        public GreetingDelegate delegate1;
        //包含調(diào)用委托的方法
        public void GreetPeople(string name) {
            if(delegate1!=null){     //如果有方法注冊委托變量
                delegate1(name);      //通過委托調(diào)用方法
            }
        }
    
    class Program{
        //具體方法1
        private static void EnglishGreeting(string name) {
            Console.WriteLine("Morning, " + name);
        }
        //具體方法2
        private static void ChineseGreeting(string name) {
            Console.WriteLine("早上好, " + name);
        }
        
        //Main(客戶端)
        static void Main(string[] args){
            //創(chuàng)建類的實(shí)例對象
            GreetingManager greetingManager = new GreetingManager();
            //給類中的委托變量屬性進(jìn)行多播委托綁定方法
            greetingManager.delegate1 = EnglishGreeting;
            greetingManager.delegate1 += ChineseGreeting;
            //調(diào)用委托(相當(dāng)于調(diào)用方法)
            greetingManager.GreetPeople("Jimmy Zhang"); 
        }
    }   
}
  • 普通委托還可以利用lambda表達(dá)式簡化寫法,如下:
//定義委托
delegate void SendMessage(string text);

//原本的一個(gè)具體的函數(shù)(委托要引用的函數(shù))
1 void WriteText(string text)
2 {
3     Console.WriteLine($"Text:{text}");
4 }
//原先委托變量賦值具體方法的寫法1
SendMessage delegate1 = new SendMessage(WriteText);
//原先委托變量賦值具體方法的寫法2
SendMessage delegate2 = WriteText;

//-------------------------------------------
//而利用lambda表達(dá)式簡化寫法后,變?yōu)槿缦碌暮啙崒懛?
SendMessage delegate3 = (text) => {Console.WriteLine($"Text:{text}");};

泛型委托

使用泛型委托可以為方法的參數(shù)類型和返回類型提供更大的靈活性和重用性,尤其是在編寫通用代碼時(shí)非常有用。


C#中定義泛型委托的語法:

delegate <return type> <delegate name><T>(<T parameters>);

//如果委托的定義符合一定的格式規(guī)范,可以省略 delegate 關(guān)鍵字


其中,<return type> 表示委托所表示方法的返回類型,<delegate name> 是委托的名稱,<T> 表示泛型類型參數(shù),<T parameters> 是泛型方法的參數(shù)列表。


以下是一個(gè)示例,展示如何定義一個(gè)簡單的泛型委托類型:

delegate T MyGenericDelegate<T>(T x);
//這個(gè)泛型委托類型的名稱是 MyGenericDelegate,它表示一個(gè)方法,該方法接受一個(gè)泛型類型的參數(shù) T 并返回一個(gè)相同T類型的值。

下面是一個(gè)使用泛型委托的示例:

// 假設(shè)有一個(gè)泛型委托,用于處理任意類型的數(shù)據(jù)
delegate void ProcessDataDelegate<T>(T data);

// 假設(shè)有一個(gè)類用于處理整數(shù)數(shù)據(jù)
class IntegerProcessor
{
    //具體的形參類型和具體方法
    public static void ProcessInteger(int number)
    {
        Console.WriteLine("Processing integer: " + number);
    }
}

// 假設(shè)有一個(gè)類用于處理字符串?dāng)?shù)據(jù)
class StringProcessor
{
    //具體的形參類型和具體方法
    public static void ProcessString(string text)
    {
        Console.WriteLine("Processing string: " + text);
    }
}

//client
class Program
{
    static void Main()
    {
        // 創(chuàng)建泛型委托實(shí)例,指向整數(shù)處理方法
        ProcessDataDelegate<int> intDelegate = IntegerProcessor.ProcessInteger;

        // 創(chuàng)建泛型委托實(shí)例,指向字符串處理方法
        ProcessDataDelegate<string> stringDelegate = StringProcessor.ProcessString;

        // 使用泛型委托處理整數(shù)數(shù)據(jù)
        intDelegate(10);

        // 使用泛型委托處理字符串?dāng)?shù)據(jù)
        stringDelegate("Hello, world!");
    }
}
//在上面的示例中,首先定義了一個(gè)泛型委托 ProcessDataDelegate<T>,它可以處理任意類型的數(shù)據(jù)。
//然后,通過創(chuàng)建委托實(shí)例并將其指向不同的處理方法,分別處理整數(shù)和字符串?dāng)?shù)據(jù)。
//最后,通過調(diào)用委托實(shí)例,將數(shù)據(jù)傳遞給相應(yīng)的處理方法進(jìn)行處理。

預(yù)定義委托類型

在 C# 中,有一些預(yù)定義的委托類型,它們屬于 System 命名空間。

這些委托類型提供了對常見委托簽名的快捷方式,減少了在聲明委托時(shí)需要手動(dòng)編寫的代碼。

以下是一些常見的預(yù)定義委托類型:

1.Action 委托

Action 委托表示不返回值的方法。它可以接受從 0 到 16 個(gè)輸入?yún)?shù)。

例如,Action<int, string> 表示一個(gè)接受整數(shù)和字符串兩個(gè)參數(shù)的方法,不返回值。

Action<int,string> myAction = (x,s) => {
    Console.WriteLine($"Received int:{x},string:{s}");
};

myAction(42,"hello");

2.Func 委托

Func 委托表示有返回值的方法。它可以接受從 0 到 16 個(gè)輸入?yún)?shù),并且最后一個(gè)參數(shù)表示返回值類型。

例如,Func<int, string, bool> 表示一個(gè)接受整數(shù)和字符串兩個(gè)參數(shù)的方法,返回一個(gè)布爾值。

Func<int, string, bool> myFunc = (x, s) =>
{
    Console.WriteLine($"Received int: {x}, string: {s}");
    return true;
};

bool result = myFunc(42, "Hello");

3.Predicate 委托

Predicate<T>Func<T, bool> 的一個(gè)特殊版本,用于表示只接受一個(gè)參數(shù)并返回布爾值的方法。

通常用于檢查某個(gè)條件是否滿足。

//由于一定返回bool值,故這里只填寫一個(gè)int參數(shù)表明接收的那一個(gè)參數(shù)的類型
Predicate<int> isEven = x => x % 2 == 0;

bool result = isEven(4);  // 返回 true

4.Comparison 委托

Comparison<T>Func<T, T, int> 的一個(gè)特殊版本,用于表示比較兩個(gè)對象的方法。通常用于排序算法中。

//這個(gè)例子中,Comparison<int> 表示一個(gè)接受兩個(gè) int 類型參數(shù)的方法,返回一個(gè)整數(shù)。
Comparison<int> compareInts = (x, y) => x.CompareTo(y);

int result = compareInts(5, 3);  // 返回 1,表示第一個(gè)參數(shù)大于第二個(gè)參數(shù);-1則第一個(gè)參數(shù)小于第二個(gè)參數(shù)

匿名類型

在C#中,**匿名類型(Anonymous Types)**是一種用于創(chuàng)建臨時(shí)對象的特殊類型。

匿名類型允許你在不顯式定義類的情況下,創(chuàng)建包含一組只讀屬性的對象。

這些屬性的名稱和類型由編譯器根據(jù)初始化表達(dá)式進(jìn)行推斷。

例子:

var person = new { Name = "John", Age = 30 };

在這個(gè)例子中,person 是一個(gè)匿名類型的對象,它有兩個(gè)只讀屬性 NameAge,這些屬性的名稱和類型由初始化表達(dá)式 "John"30 推斷而來。

特點(diǎn)和限制:

  1. 只讀屬性: 匿名類型的屬性是只讀的,無法在初始化后修改。
  2. 屬性推斷: 屬性的名稱和類型是由初始化表達(dá)式推斷而來的,而不是顯式聲明的。
  3. 編譯時(shí)類型: 對于不同的初始化表達(dá)式,編譯器會(huì)創(chuàng)建不同的匿名類型,即使屬性的名稱和類型相同。

匿名類型通常在需要在一個(gè)地方使用臨時(shí)數(shù)據(jù)結(jié)構(gòu)而不想專門為之創(chuàng)建一個(gè)類時(shí)很有用

示例1:例如在LINQ查詢中選擇特定的字段。

//LINQ查詢(聲明語法)
var query = from p in people
            select new { p.Name, p.Age };
//在這個(gè)例子中,`query` 是一個(gè)匿名類型的集合,包含了 `people` 集合中每個(gè)對象的 `Name` 和 `Age` 屬性。

示例2:

var person = new { Name = "John", Age = 30 };

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");

//這個(gè)例子中,`person` 是一個(gè)匿名類型的對象,可以通過屬性名訪問其屬性值。

匿名函數(shù)

在C#中,匿名函數(shù)是一種無需命名的函數(shù),可以在代碼中定義和使用。

匿名函數(shù)通常用作委托的實(shí)際參數(shù),以便在運(yùn)行時(shí)將其傳遞給其他函數(shù)或方法。


C#中有兩種類型的匿名函數(shù):lambda表達(dá)式和匿名方法。


匿名方法

匿名方法是一種匿名函數(shù)。

匿名方法是一種使用delegate關(guān)鍵字定義的無需命名的方法。

它的語法如下:

delegate (parameter_list) { statements }


其中,parameter_list是一個(gè)用逗號(hào)分隔的參數(shù)列表,statements是一系列語句。

例如,以下代碼創(chuàng)建了一個(gè)匿名方法,將兩個(gè)整數(shù)相加并返回結(jié)果:

Func<int, int, int> add = delegate(int x, int y) { return x + y; };//其中的Func<int,int,int>是泛型委托,它表示一個(gè)能夠接受兩個(gè)整數(shù)參數(shù)并返回一個(gè)整數(shù)結(jié)果的委托
//上面的代碼也可寫成: Func<int,int,int> add = delegate(x,y) {return x+y;}
//即可以省略形參的類型,因?yàn)橥ㄓ梦蠪unc中已經(jīng)表明了方法形參的數(shù)據(jù)類型
int result = add(3, 4); // result = 7

lambda表達(dá)式

Lambda表達(dá)式是一種匿名函數(shù),它可以快速地創(chuàng)建一個(gè)委托或表達(dá)式。

lambda 表達(dá)式是從匿名方法演變而來,首先刪除了委托關(guān)鍵字(delegate)和參數(shù)類型,并添加了一個(gè) lambda 運(yùn)算符 =>。

它的語法如下:

(parameter_list) => {expression}

  • 如果只有一個(gè)參數(shù),參數(shù)列表(parameter_list)的()可以省略。

  • 如果方法體只有一行代碼,方法體{expression}中可省略{}。

  • 如果=>之后的方法體中只有一行代碼,且方法有返回值,那么可以省略方法體的{}以及return


其中,parameter_list是一個(gè)用逗號(hào)分隔的參數(shù)列表,expression是一個(gè)返回值的表達(dá)式。

例如,以下代碼創(chuàng)建了一個(gè)lambda表達(dá)式,將兩個(gè)整數(shù)相加并返回結(jié)果:

Func<int, int, int> add = (x, y) => x + y;//其中的Func<int,int,int>是泛型委托,它表示一個(gè)能夠接受兩個(gè)整數(shù)參數(shù)并返回一個(gè)整數(shù)結(jié)果的委托
//可以省略參數(shù)數(shù)據(jù)類型,因?yàn)榫幾g器根據(jù)委托類型推斷出參數(shù)類型用=>引出來方法體
int result = add(3, 4); // result = 7

擴(kuò)展方法

什么是擴(kuò)展方法?

擴(kuò)展方法,顧名思義,是對原有的對象附加一個(gè)新的方法。

可以將擴(kuò)展方法添加到您自己的自定義類、.NET 框架類或第三方類或接口。

如何區(qū)分?jǐn)U展方法?

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

如上圖,擴(kuò)展方法在visual studio的intellisense中有一個(gè)特殊的符號(hào),這樣你就可以輕松區(qū)分類方法和擴(kuò)展方法。


自己如何寫擴(kuò)展方法

在以下示例中,我在 Malema.net 命名空間下創(chuàng)建了一個(gè) IntExtensions 類。

IntExtensions 類中將包含適用于 int 數(shù)據(jù)類型的所有擴(kuò)展方法。

(可以為命名空間和類使用任何名稱。有時(shí)候我們會(huì)命名成我們項(xiàng)目當(dāng)中都要用到的名字,這樣就不需在每個(gè)類里再引用一次命名空間)

namespace Malema.Extensions
{
    public static class IntExtensions
    {

    }
}

現(xiàn)在,將靜態(tài)方法定義為擴(kuò)展方法,其中擴(kuò)展方法的第一個(gè)參數(shù)指定擴(kuò)展方法所適用的類型。 我們將在 int 類型上使用這個(gè)擴(kuò)展方法。 所以第一個(gè)參數(shù)必須在 int 前面加上 this 修飾符。除了第一個(gè)參數(shù),其他參數(shù)就是真正的方法形參。

namespace Malema.Extensions
{
    public static class IntExtensions
    {
        public static bool IsGreaterThan(this int i, int value)
        {
            return i > value;
        }
    }
}

現(xiàn)在,可以在要使用此擴(kuò)展方法的地方 引用 Malema.Extensions 命名空間即可。

using System;
using Malema.Extensions;

namespace ConsoleApp{
    class program{
        public static void Main(String[] args){
            var i = 10;
            bool result = i.IsGreaterThan(100);
            Console.WriteLine(result);       
        }
    }
}

寫一個(gè)泛型擴(kuò)展方法

namespace Malema.Extensions{
    public static class NumberExtensions{
        public static bool IsGreaterThan<T>(this T i,T value) where T:IComparable{
            return i.CompareTo(value)?false:true;
        }
    }
}

總結(jié):

常規(guī)靜態(tài)方法和擴(kuò)展方法之間的唯一區(qū)別是擴(kuò)展方法的第一個(gè)參數(shù)指定要對其進(jìn)行操作的類型,前面是 this 關(guān)鍵字。


  • 擴(kuò)展方法是額外的自定義方法,它們最初不包含在類中。
  • 可以將擴(kuò)展方法添加到自定義、.NET Framework 或第三方類、結(jié)構(gòu)或接口。
  • 擴(kuò)展方法的第一個(gè)參數(shù)必須是擴(kuò)展方法適用的類型,以 this 關(guān)鍵字開頭。
  • 通過包含引入擴(kuò)展方法的命名空間,可以在應(yīng)用程序的任何地方使用擴(kuò)展方法。

對象初始化

C#中的對象初始化語法(Object Initializer Syntax)允許你在創(chuàng)建對象的同時(shí)對其屬性進(jìn)行初始化,而不止是單靠構(gòu)造函數(shù),使得代碼更為簡潔。這種語法通常用于設(shè)置對象的屬性值,特別是在構(gòu)造函數(shù)的參數(shù)列表中沒有足夠信息來初始化對象時(shí)。

在C#中,對象初始化語法通常是通過使用對象初始化器(Object Initializer Syntax)完成的,而不是直接調(diào)用構(gòu)造函數(shù)。

這意味著你可以在創(chuàng)建對象的同時(shí)初始化其屬性,而不必使用括號(hào)調(diào)用構(gòu)造函數(shù)

1. 基本用法:

// 定義一個(gè)類
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 使用對象初始化語法創(chuàng)建并初始化對象
Person person = new Person
{
    Name = "John Doe",
    Age = 30
};

2. 初始化匿名類型:

對象初始化語法不僅適用于具體的類,還可以用于初始化匿名類型。

var person = new { Name = "John Doe", Age = 30 };

3. 嵌套初始化:

如果類中包含其他對象的引用,也可以使用對象初始化語法為嵌套的對象進(jìn)行初始化。

public class Address
{
    public string City { get; set; }
    public string Country { get; set; }
}

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Address HomeAddress { get; set; }
}

Person person = new Person
{
    Name = "John Doe",
    Age = 30,
    HomeAddress = new Address
    {
        City = "New York",
        Country = "USA"
    }
};

4. 在集合中使用初始化語法:

在集合的初始化中,你也可以使用對象初始化語法來初始化集合中的對象。

List<Person> people = new List<Person>
{
    new Person { Name = "John Doe", Age = 30 },
    new Person { Name = "Jane Doe", Age = 25 }
};

5. 使用構(gòu)造函數(shù)和初始化語法:

如果類有構(gòu)造函數(shù),你可以在初始化語法中使用構(gòu)造函數(shù),并在大括號(hào)中為屬性賦值。

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

Person person = new Person("John Doe", 30);

總的來說,對象初始化語法提供了一種簡潔而方便的方式來創(chuàng)建和初始化對象。

注意:

  • 在Java中,沒有像C#那樣的對象初始化語法,因此對象的初始化主要通過構(gòu)造函數(shù)來完成。

  • 在C#中,對象初始化語句(Object Initialization Syntax)可以使用帶括號(hào)和不帶括號(hào)的方式。

    ClassName obj = new ClassName {
        Property1 = value1,
        Property2 = value2,
        // ...
    };
    
    ClassName obj = new ClassName() {
        Property1 = value1,
        Property2 = value2,
        // ...
    };
    

    無論你選擇使用括號(hào)還是不使用括號(hào),兩者都會(huì)創(chuàng)建一個(gè)新的對象并進(jìn)行初始化。在實(shí)際使用中,括號(hào)通常是可選的,除非構(gòu)造函數(shù)本身需要參數(shù)。如果構(gòu)造函數(shù)不需要參數(shù),則可以選擇省略括號(hào)。


繼承與多態(tài)

  1. 多態(tài)的理解: 同一類/類型的多個(gè)實(shí)例對同一個(gè)消息做出不同的反應(yīng)。

    在平時(shí)的應(yīng)用中,多態(tài)性就是指,當(dāng)使用基類對象指向子類實(shí)例對象時(shí),使用這個(gè)對象去調(diào)用基類和父類中的同名方法。

    總的來說,通過基類類型指向派生類對象時(shí),如果方法被重寫,將調(diào)用派生類中的方法;如果方法沒有被重寫,將調(diào)用基類中的方法。這種行為體現(xiàn)了多態(tài)性的概念。
    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  2. 關(guān)于c#中用基類變量聲明子類實(shí)例對象,在調(diào)用基類和子類中的同名方法時(shí)的調(diào)用結(jié)果問題:

    • case1: 當(dāng)沒碰到重寫時(shí),不管子類中的同名方法上有沒有帶有new關(guān)鍵字,用實(shí)例對象.同名方法調(diào)用的最終都是基類中的同名方法,即方法的隱藏。在這種情況下,派生類的方法會(huì)隱藏自己跟基類中的同名方法,使得只會(huì)調(diào)用基類中的方法。
    • case2: 當(dāng)碰到重寫時(shí),即基類同名方法帶有virtual關(guān)鍵字,子類同名方法帶有override關(guān)鍵字時(shí),此時(shí)用實(shí)例對象.同名方法調(diào)用的最終都是子類中同名方法
  3. C#中的繼承僅支持單一繼承。

    繼承的語法格式: 當(dāng)前類名:繼承的基類名

  4. 組合優(yōu)于繼承。

    繼承的一個(gè)壞處繼承層次多的時(shí)候。如果你想改動(dòng)底層的一個(gè)方法的時(shí)候。你得考慮子類的依賴性。

  5. C#中若不想一個(gè)類被繼承,可以添加sealed關(guān)鍵字。

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  6. C#中所有類型都繼承自System.Object(隱式繼承,即沒有明顯的寫出xx類 : System.Object的形式).

  7. C#中的base關(guān)鍵字類似于Java中的super關(guān)鍵字。

  8. 子類重寫基類方法時(shí),會(huì)涉及到virtualoverride關(guān)鍵字。

    其中基類的方法會(huì)添加virtual關(guān)鍵字,而子類的同名方法會(huì)添加override關(guān)鍵字。

    【而對比Java通過 @Override 注解實(shí)現(xiàn)方法重寫】


    virtual修飾符:virtual修飾符可以將一個(gè)方法聲明為虛擬方法。

    即聲明當(dāng)前的方法只是一個(gè)占位符,具體的操作都得靠其派生類來實(shí)現(xiàn)。

    虛方法的作用在于允許使用多態(tài)性,提高靈活性和擴(kuò)展性; 允許在派生類中覆蓋基類的方法,提供定制的實(shí)現(xiàn)。

    其中virtual使用的語法格式:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    其中override使用的語法格式:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

抽象類與接口

抽象類:

  • 方法用abstract關(guān)鍵字修飾,則類也要用abstract關(guān)鍵字修飾。

接口:

  • 接口可以實(shí)現(xiàn)多個(gè)。
  • 接口名常以大寫I開頭,如:IMyInterface
  • 接口的實(shí)現(xiàn)格式: 類名:接口名

注意:

由于繼承和接口實(shí)現(xiàn)都是用:的格式,

當(dāng)碰到某個(gè)類既要繼承某個(gè)類,還有實(shí)現(xiàn)某個(gè)接口時(shí),此時(shí)繼承類寫在前面,實(shí)現(xiàn)的接口寫在后面,中間以,隔開:

如: class Manager:Employee,IMyInterface

因?yàn)橐粋€(gè)類繼承的類只能有一個(gè),但實(shí)現(xiàn)的接口可以有多個(gè)。

抽象類和接口的使用選擇:

  • 當(dāng)涉及到繼承/復(fù)用時(shí),考慮抽象類,否則優(yōu)先考慮接口。

C#特性

特性Attribute是用于在運(yùn)行時(shí)傳遞程序中各種元素(比如類、方法、結(jié)構(gòu)、枚舉、組件等)的行為信息的聲明性標(biāo)簽。

其它語言如java中也叫注解 anotation

您可以通過使用特性向程序添加聲明性信息。

一個(gè)聲明性標(biāo)簽是通過放置在它所應(yīng)用的元素前面的方括號(hào)([ ])來描述的。

特性(Attribute)用于添加元數(shù)據(jù),如編譯器指令和注釋、描述、方法、類等其他信息。

.Net 框架提供了兩種類型的特性:預(yù)定義特性和自定義特性。

預(yù)定義特性

Net 框架提供了三種預(yù)定義特性:

  • AttributeUsage
  • Conditional
  • Obsolete

AttributeUsage

預(yù)定義特性 AttributeUsage 描述了如何使用一個(gè)自定義特性類。

它規(guī)定了特性可應(yīng)用到的項(xiàng)目的類型。

當(dāng)我們要自己定義Attribute的時(shí)候我們就需要用到它了。

規(guī)定該特性的語法如下:

[AttributeUsage(validon,AllowMultiple=allowmultiple,Inherited=inherited)]

其中:

  • 參數(shù) validon 規(guī)定特性可被放置的語言元素。它是枚舉器 AttributeTargets 的值的組合。默認(rèn)值是 AttributeTargets.All。
  • 參數(shù) allowmultiple(可選的)為該特性的 AllowMultiple 屬性(property)提供一個(gè)布爾值。如果為 true,則該特性是多用的。默認(rèn)值是 false(單用的)。
  • 參數(shù) inherited(可選的)為該特性的 Inherited 屬性(property)提供一個(gè)布爾值。如果為 true,則該特性可被派生類繼承。默認(rèn)值是 false(不被繼承)。 例如:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]

條件編譯 Conditional

這個(gè)預(yù)定義特性標(biāo)記了一個(gè)條件方法,其執(zhí)行依賴于指定的預(yù)處理標(biāo)識(shí)符。

它會(huì)引起方法調(diào)用的條件編譯,取決于指定的值,比如 Debug 或 Trace。

例如,當(dāng)調(diào)試代碼時(shí)顯示變量的值。規(guī)定該特性的語法如下:

[Conditional(conditionalSymbol)]

示例:

namespace Malema.net
{
    public class Myclass
    {
        [Conditional("DEBUG")]
        public static void Message(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class Program
    {
        static async Task Main(string[] args)
        {
            Myclass.Message("hello");
            Console.ReadLine();
        }
    }
}

上面的代碼在Debug模式下,會(huì)輸出 hello。在Release模式下則不會(huì)輸出hello

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

Obsolete

這個(gè)預(yù)定義特性標(biāo)記了不應(yīng)被使用的程序?qū)嶓w。它可以讓您通知編譯器丟棄某個(gè)特定的目標(biāo)元素。

例如,當(dāng)一個(gè)新方法被用在一個(gè)類中,但是您仍然想要保持類中的舊方法,您可以通過顯示一個(gè)應(yīng)該使用新方法而不是舊方法的消息,來把它標(biāo)記為 obsolete(過時(shí)的)。

規(guī)定該特性的語法如下:

[Obsolete(message)]
[Obsolete(message,iserror)]

其中:

  • 參數(shù) message,是一個(gè)字符串,描述項(xiàng)目為什么過時(shí)以及該替代使用什么。
  • 參數(shù) iserror,是一個(gè)布爾值。如果該值為 true,編譯器應(yīng)把該項(xiàng)目的使用當(dāng)作一個(gè)錯(cuò)誤。默認(rèn)值是 false(編譯器生成一個(gè)警告)。 下面的實(shí)例演示了該特性:
[Obsolete("過期了,請使用YourClass.Abc")]
    public class Myclass
    {
        public static void Message(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class Program
    {
        static async Task Main(string[] args)
        {
            Myclass.Message("helo");
            Console.ReadLine();
        }
    }

會(huì)看到如下圖這樣的編譯警告提示:

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

自定義特性

Net 框架允許創(chuàng)建自定義特性,用于存儲(chǔ)聲明性的信息,且可在運(yùn)行時(shí)被檢索。

創(chuàng)建并使用自定義特性包含三個(gè)步驟:

  • 聲明自定義特性
  • 在目標(biāo)程序元素上應(yīng)用自定義特性
  • 通過反射訪問特性 最后一個(gè)步驟包含編寫一個(gè)簡單的程序來讀取元數(shù)據(jù)以便查找各種符號(hào)。元數(shù)據(jù)是用于描述其他數(shù)據(jù)的數(shù)據(jù)和信息。該程序應(yīng)使用反射來在運(yùn)行時(shí)訪問特性。

聲明自定義特性

一個(gè)新的自定義特性應(yīng)派生自 System.Attribute 類。例如:

[AttributeUsage(AttributeTargets.Property)]
public class CsvAttritube:Attribute{
    public string name{get;set;}
    public CsvAttribute(string name){
        this.name = name;
    }
}

應(yīng)用這個(gè)特性

上面我們自定義的CsvAttribute類。但是在使用的時(shí)候我們可以把Attribute省略掉。

public class Bar{
    [Csv("OpenPrice")
    public float Open{get;set};
    [Csv("ClosePrice")]
    public float Close{get;set;}
}

如何讓這個(gè)特性有效果。得靠反射。

C# 反射

反射這個(gè)東西 就如同照鏡子一樣:鏡子里面反射出了正在照鏡子的人。

反射跟這個(gè)的效果就是一樣的:通過反射程序我們就可以知道程序是什么樣子的。

反射是.Net中獲取運(yùn)行時(shí)類型信息的方式。

.Net的應(yīng)用程序由幾個(gè)部分:程序集(Assembly)、模塊(Module)類型(class)組成,而反射提供一種編程的方式,讓程序員可以在程序運(yùn)行期獲得這幾個(gè)組成部分的相關(guān)信息.

通過使用反射API

  • 我們可以用反射訪問到 程序集 模塊 類型 與及類型上面的一些元信息 Attribute, 這些統(tǒng)稱為元數(shù)據(jù)(metadata)。

  • 我們還可以使用反射 動(dòng)態(tài)的創(chuàng)建對象,并對對象的屬性字段進(jìn)行取值和賦值,也可以調(diào)用里面的方法,包括私有方法。

反射優(yōu)點(diǎn)

  1. 反射提高了程序的靈活性和擴(kuò)展性。
  2. 降低耦合性,提高自適應(yīng)能力。
  3. 它允許程序創(chuàng)建和控制任何類的對象,無需提前硬編碼目標(biāo)類。

反射缺點(diǎn)

  1. 性能問題:使用反射基本上是一種解釋操作,用于字段和方法接入時(shí)要遠(yuǎn)慢于直接代碼。

    因此反射機(jī)制主要應(yīng)用在對靈活性和拓展性要求很高的系統(tǒng)框架上,普通程序不建議使用。

  2. 使用反射會(huì)模糊程序內(nèi)部邏輯;程序員希望在源代碼中看到程序的邏輯,反射卻繞過了源代碼的技術(shù),因而會(huì)帶來維護(hù)的問題,反射代碼比相應(yīng)的直接代碼更復(fù)雜。


反射用到的主要類:

System.Reflection.Assembly類

Assembly 類是 C# 中用于處理程序集(Assembly)的主要類之一。

它提供了獲取程序集信息、訪問類型和成員、以及執(zhí)行程序集中的代碼的方法。

以下是 Assembly 類的一些常用方法和示例:

1.加載程序集

// 加載當(dāng)前執(zhí)行的程序集
Assembly currentAssembly = Assembly.GetExecutingAssembly();

// 加載指定的程序集
Assembly myAssembly = Assembly.LoadFrom("MyAssembly.dll");

2.獲取程序集信息:

// 獲取當(dāng)前執(zhí)行的程序集信息
Assembly currentAssembly = Assembly.GetExecutingAssembly();
Console.WriteLine($"Assembly Name: {currentAssembly.FullName}");

// 獲取程序集的類型
Type[] types = currentAssembly.GetTypes();
foreach (Type type in types)
{
    Console.WriteLine($"Type: {type.FullName}");
}

3.獲取程序集中的類型和成員:

// 獲取當(dāng)前執(zhí)行的程序集
Assembly currentAssembly = Assembly.GetExecutingAssembly();

// 獲取程序集中的類型
Type myType = currentAssembly.GetType("MyNamespace.MyClass");

// 獲取類型中的成員
MethodInfo method = myType.GetMethod("MyMethod");
PropertyInfo property = myType.GetProperty("MyProperty");

4.執(zhí)行程序集中的代碼:

// 獲取當(dāng)前執(zhí)行的程序集
Assembly currentAssembly = Assembly.GetExecutingAssembly();

// 創(chuàng)建對象實(shí)例
object myObject = Activator.CreateInstance(currentAssembly.GetType("MyNamespace.MyClass"));

// 調(diào)用方法:Invoke(object obj, object[] parameters);
//obj:表示要在其上調(diào)用方法的對象實(shí)例;parameters:是一個(gè)數(shù)組,包含傳遞給方法的參數(shù)
MethodInfo method = currentAssembly.GetType("MyNamespace.MyClass").GetMethod("MyMethod");
method.Invoke(myObject, null);

System.Activator

Activator 類是 C# 中的一個(gè)工具類,它提供了在運(yùn)行時(shí)創(chuàng)建對象、調(diào)用構(gòu)造函數(shù)以及創(chuàng)建對象實(shí)例的功能。Activator 類主要包含一些靜態(tài)方法,方便在不知道具體類型的情況下動(dòng)態(tài)地創(chuàng)建對象。

以下是 Activator 類的一些常用方法和示例:

1.CreateInstance 方法: 通過指定類型的名稱或指定類型的Type 對象,創(chuàng)建該類型的實(shí)例

// 通過類型名稱創(chuàng)建對象實(shí)例
object obj1 = Activator.CreateInstance(Type.GetType("System.String"));

// 通過 Type 對象創(chuàng)建對象實(shí)例
Type myType = typeof(int);
object obj2 = Activator.CreateInstance(myType);

2.CreateInstance<T> 泛型方法: 通過指定類型的名稱或 指定類型的Type 對象,創(chuàng)建該類型的實(shí)例,并進(jìn)行強(qiáng)類型轉(zhuǎn)換

// 通過類型名稱創(chuàng)建對象實(shí)例并進(jìn)行強(qiáng)類型轉(zhuǎn)換
string str = Activator.CreateInstance<string>();

// 通過 Type 對象創(chuàng)建對象實(shí)例并進(jìn)行強(qiáng)類型轉(zhuǎn)換
Type myType = typeof(int);
int num = Activator.CreateInstance<int>();

3.CreateInstanceFrom 方法: 從指定程序集文件中創(chuàng)建對象實(shí)例

Assembly myAssembly = Assembly.LoadFrom("MyAssembly.dll");
Type myType = myAssembly.GetType("MyNamespace.MyClass");

object obj = Activator.CreateInstanceFrom("MyAssembly.dll", "MyNamespace.MyClass").Unwrap();

Console.WriteLine(obj.GetType());  // 輸出: MyNamespace.MyClass
}

System.Type類:

System.Type 類對于反射起著核心的作用。

它是一個(gè)抽象的基類,Type有與每種數(shù)據(jù)類型對應(yīng)的派生類,我們使用這個(gè)派生類的對象的方法、字段、屬性查找有關(guān)該類型的所有信息。

獲取給定類型的Type引用有3種常用方式:

  • 使用c# typeof運(yùn)算符

    Type t = typeof(string);
    
  • 使用對象的GetType()方法

    string s = "hello,c#";
    Type t = s.GetType();
    
  • 調(diào)用Type類的靜態(tài)方法GetType()

    Type t = 	Type.GetType(System.String);
    

上面的三種方式都是獲取string類型的Type引用對象,在取出string類型的Type引用t之后, 就可以通過t來探測string的類型結(jié)構(gòu),如下:

string s = "carson";
Type t = s.GetType();

foreach(MememInfo mi in t.GetMembers()){
    Console.WriteLine(mi.Name);
}

Type類的屬性:

  • Name 數(shù)據(jù)類型名

  • FullName 數(shù)據(jù)類型的完全限定名(包括命名空間名)

  • Namespace 定義數(shù)據(jù)類型的命名空間名

  • IsAbstract 指示該類型是否是抽象類型

  • IsArray 指示該類型是否是數(shù)組

  • IsClass 指示該類型是否是類

  • IsEnum 指示該類型是否是枚舉

  • IsInterface 指示該類型是否是接口

  • IsPublic 指示該類型是否是公有的

  • IsSealed 指示該類型是否是密封類

  • IsValueType 指示該類型是否是值類型

Type類的方法:

  • GetConstructor() : GetConstructors()返回ConstructorInfo類型,用于取得該類的構(gòu)造函數(shù)的信息
  • GetEvent(): GetEvents()返回EventInfo類型,用于取得該類的事件的信息
  • GetField(): GetFields()返回FieldInfo類型,用于取得該類的字段(成員變量)的信息
  • GetInterface(): GetInterfaces()返回InterfaceInfo類型,用于取得該類實(shí)現(xiàn)的接口的信息
  • GetMember(): GetMembers()返回MemberInfo類型,用于取得該類的所有成員的信息
  • GetMethod(): GetMethods()返回MethodInfo類型,用于取得該類的方法的信息
  • GetProperty(): GetProperties()返回PropertyInfo類型,用于取得該類的屬性的信息
  • 可以調(diào)用這些成員,其方式是調(diào)用Type的InvokeMember()方法,或者調(diào)用MethodInfo, PropertyInfo和其他類的Invoke()方法。

反射的具體使用

(1)首先定義一個(gè)類用來測試反射的各種常見操作

public class NewClass
    {	
    	//定義各個(gè)字段
        public string a;
        public int b;
        public string Name { get; set; }
        public int Age { get; set; }
    	//構(gòu)造函數(shù)1
        public NewClass(string a,int b)
        {
            this.a = a;
            this.b = b;
        }
		//構(gòu)造函數(shù)2
        public NewClass()
        {
            Console.WriteLine("調(diào)用構(gòu)造函數(shù)");
        }
		//show方法,顯示具體的字段值
        public void show()
        {
            Console.WriteLine("生成一個(gè)對象成功,其中a是:" + a +" b是:"+ b +" Name是:"+ this.Name+" Age是:" + this.Age);
        }
    }

(2)利用反射查看類中的構(gòu)造方法:

//client
{
    //測試類的對象實(shí)例化
    NewClass nc = new NewClass();
    //獲取Type的引用
    Type t = nc.GetType();
    //獲取所有的構(gòu)造器
    ConstructorInfo[] ci = t.GetConstructors();
    //遍歷構(gòu)造器
    foreach (var c in ci)
    {
        Console.WriteLine("count");
        //獲取構(gòu)造器中的形參信息
        ParameterInfo[] pi = c.GetParameters();
        //遍歷形參信息
        foreach (ParameterInfo p in pi)
        {
            //Write輸出不換行
            Console.Write("形參類型:" + p.ParameterType.ToString() + " |" + "形參名字:" + p.Name + ",");
        }
        //WriteLine輸出換行
        Console.WriteLine();
    }
}

(3)用構(gòu)造函數(shù)動(dòng)態(tài)生成對象

//client
{	
    //獲取Type的引用
    Type t = typeof(NewClass);
    //根據(jù)對應(yīng)構(gòu)造器的形參類型,構(gòu)造Types數(shù)組
    Type[] types = new Type[2];
    types[0] = typeof(string);
    types[1] = typeof(int);
    //傳入Types數(shù)組,找到對應(yīng)的構(gòu)造函數(shù)
    ConstructorInfo constructorInfo = t.GetConstructor(types);
    //根據(jù)對應(yīng)構(gòu)造器對應(yīng)的實(shí)參值,構(gòu)造object數(shù)組
    object[] objs = new object[2] { "5", 6 };
    //傳入object數(shù)組,調(diào)用構(gòu)造函數(shù)生成對象
    object o = constructorInfo.Invoke(objs);
    //對應(yīng)的對象字段賦值
    ((NewClass)o).Name = "carson";
    ((NewClass)o).Age = 20;
    //調(diào)用對應(yīng)對象的方法
    ((NewClass)o).show();
}

(4)用Activator生成對象**【利用Activator的CreateInstance靜態(tài)方法**】

Activator 類在反射中使用時(shí),由于是在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建對象,因此性能可能不如直接使用構(gòu)造函數(shù)。

在大部分情況下,最好直接使用類型的構(gòu)造函數(shù)來創(chuàng)建對象,而只在必要時(shí)才使用 Activator 類的功能

//client
{
    //獲取相應(yīng)類的Type引用
    Type t = typeof(NewClass);
    //法一:構(gòu)造函數(shù)的參數(shù)
    object[] objs = new object[2] { "hello", 110 };
    //法一:用Activator的CreateInstance靜態(tài)方法(調(diào)用有參構(gòu)造方法,傳入object數(shù)組),生成新對象
    object o = Activator.CreateInstance(t,objs);
    //法二:用Activator的CreateInstance靜態(tài)方法(調(diào)用有參構(gòu)造方法,不直接傳入object數(shù)組),生成新對象
    object o = Activator.CreateInstance(t, "hello", 10);

    //法三:用Activator的CreateInstance靜態(tài)方法(調(diào)用無參的構(gòu)造方法)
    object o = Activator.CreateInstance(t);
    //調(diào)用生成對象的方法
    ((NewClass)o).Name = "carson";
    ((NewClass)o).Age = 20;
    ((NewClass)o).show();
}

(5)查看類中的屬性(Property)

//獲取相應(yīng)類的Type引用
Type t = typeof(NewClass);
//獲取屬性
PropertyInfo[] propertyInfos = t.GetProperties();
//遍歷輸出屬性
foreach(PropertyInfo pi in propertyInfos)
{
    Console.WriteLine(pi.Name);
}

(6)查看類中的public方法

//獲取相應(yīng)類的Type引用
Type t = typeof(NewClass);
//獲取方法
MethodInfo[] methodInfos = t.GetMethods();
//遍歷輸出方法
foreach(MethodInfo mi in methodInfos)
{
    Console.WriteLine(mi.ReturnType+" "+mi.Name);
}

(7)查看類中的public字段(Field)

//獲取相應(yīng)類的Type引用
Type t = typeof(NewClass);
//獲取Public字段
FieldInfo[] fieldInfos = t.GetFields();
//遍歷輸出字段
foreach(FieldInfo fi in fieldInfos)
{
    Console.WriteLine(fi.Name);
}

(8)用反射生成對象,并調(diào)用屬性、方法和字段進(jìn)行操作

//獲取相應(yīng)類的Type引用
Type t = typeof(NewClass);
//獲取Public字段
object obj = Activator.CreateInstance(t);
//取得a字段
FieldInfo aField = t.GetField("a");
//給a字段賦值[傳入:實(shí)例對象,賦值的具體值]
aField.SetValue(obj, "hello");
//取得b字段
FieldInfo bField = t.GetField("b");
//給a字段賦值
bField.SetValue(obj, 20);

//取得Name屬性
PropertyInfo NameProperty = t.GetProperty("Name");
//給Name屬性賦值
NameProperty.SetValue(obj, "Carson");

//取得Age屬性
PropertyInfo AgeProperty = t.GetProperty("Age");
//給Age屬性賦值
AgeProperty.SetValue(obj, 22);

//取得show方法
MethodInfo mi = t.GetMethod("show");
//調(diào)用show方法
mi.Invoke(obj, null);

c# 預(yù)處理指令

在C#中,預(yù)處理指令是用于在代碼編譯之前執(zhí)行一些特定操作的指令,它們以#字符開頭,不以;結(jié)尾。

這些指令用于控制編譯過程,并可以在代碼中進(jìn)行條件編譯,根據(jù)不同的條件選擇性地包含或排除代碼。

以下是一些常見的C#預(yù)處理指令及其示例:

  1. #define#undef

    • #define 用于定義一個(gè)符號(hào),可以在代碼中用于條件編譯。
    • #undef 用于取消已定義的符號(hào)。
    #define DEBUG
    using System;
    
    class Program
    {
        static void Main()
        {
    #if DEBUG
            Console.WriteLine("Debug mode is enabled.");
    #else
            Console.WriteLine("Debug mode is not enabled.");
    #endif
    
    #undef DEBUG
    
    #if DEBUG
            Console.WriteLine("This will not be compiled.");
    #else
            Console.WriteLine("Debug mode is not enabled.");
    #endif
        }
    }
    
  2. #if#elif#else

    • #if 用于根據(jù)符號(hào)是否已定義來包含或排除代碼塊。
    • #elif 用于在多個(gè)條件之間選擇一個(gè)。
    • #else 用于在沒有任何條件匹配時(shí)執(zhí)行。
    #define DEBUG
    #define VERSION1
    
    using System;
    
    class Program
    {
        static void Main()
        {
    #if (DEBUG && !VERSION1)
            Console.WriteLine("Debug mode is enabled.");
    #elif (!DEBUG && VERSION1)
            Console.WriteLine("Version 1 is enabled.");
    #else
            Console.WriteLine("Debug mode is not enabled, and Version 1 is not enabled.");
    #endif
        }
    }
    
  3. #warning#error

    • #warning 用于生成警告消息。
    • #error 用于生成編譯錯(cuò)誤消息。
    #define VERSION1
    
    using System;
    
    class Program
    {
        static void Main()
        {
    #if !VERSION1
    #warning This code is for a different version.
    #error This code should not be compiled for the current version.
    #endif
    
            Console.WriteLine("Some code here.");
        }
    }
    

這些預(yù)處理指令允許您在不同的條件下控制代碼的編譯行為,這在處理不同配置和環(huán)境的代碼時(shí)非常有用。

請注意,預(yù)處理指令在編譯時(shí)處理,因此編譯后的可執(zhí)行文件將不包含未滿足條件的代碼塊。這有助于優(yōu)化和定制您的應(yīng)用程序的構(gòu)建。

C#結(jié)構(gòu)體

在 C# 中,struct 是表示數(shù)據(jù)結(jié)構(gòu)的值類型數(shù)據(jù)類型。它們在棧上分配內(nèi)存,而不是在堆上,這有助于提高性能。然而,由于是值類型,結(jié)構(gòu)體的拷貝會(huì)導(dǎo)致值的復(fù)制,因此在某些情況下可能不適用于大型對象。

它可以包含參數(shù)化構(gòu)造函數(shù)、靜態(tài)構(gòu)造函數(shù)、常量、字段、方法、屬性、索引器、運(yùn)算符、事件和嵌套類型。

struct 可用于保存不需要繼承的小數(shù)據(jù)值,例如 坐標(biāo)點(diǎn)、鍵值對和復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。

結(jié)構(gòu)體聲明

使用 struct 關(guān)鍵字聲明結(jié)構(gòu)。 默認(rèn)訪問修飾符是internal

以下示例聲明了一個(gè)結(jié)構(gòu) Coordinate。

struct Coordinate
{
    public int x;
    public int y;
}

//使用 new 關(guān)鍵字創(chuàng)建了 Coordinate 結(jié)構(gòu)的對象。 
//它調(diào)用結(jié)構(gòu)的默認(rèn)無參數(shù)構(gòu)造函數(shù),該構(gòu)造函數(shù)將所有成員初始化為其指定數(shù)據(jù)類型的默認(rèn)值。
var point = new Coordinate();
Console.WriteLine(point.x); //輸出: 0  
Console.WriteLine(point.y); //輸出: 0  

//-------------------------------------------
//如果在不使用 new 關(guān)鍵字的情況下聲明 struct 類型的變量,則它不會(huì)調(diào)用任何構(gòu)造函數(shù),因此所有成員都保持未分配狀態(tài)。 因此,您必須在訪問它們之前為每個(gè)成員賦值,否則會(huì)產(chǎn)生編譯時(shí)錯(cuò)誤。
struct Coordinate
{
    public int x;
    public int y;
}

Coordinate point;
Console.Write(point.x); // 編譯錯(cuò)誤

point.x = 10;
point.y = 20;
Console.Write(point.x); //輸出: 10  
Console.Write(point.y); //輸出: 20  

結(jié)構(gòu)體的構(gòu)造器

結(jié)構(gòu)不能包含無參數(shù)構(gòu)造函數(shù)。 它只能包含參數(shù)化構(gòu)造函數(shù)或靜態(tài)構(gòu)造函數(shù)。

struct Coordinate
{
    public int x;
    public int y;
	
    //必須在參數(shù)化構(gòu)造函數(shù)中包含結(jié)構(gòu)體的所有成員,并為它賦值; 否則,如果任何成員未配賦值的話,C# 編譯器將給出編譯時(shí)錯(cuò)誤。
    public Coordinate(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    
    public static Coordinate GetOrigin()
    {
        return new Coordinate();
    }
}

Coordinate point = new Coordinate(10, 20);

Console.WriteLine(point.x); //輸出: 10  
Console.WriteLine(point.y); //輸出: 20  


//---------------
Coordinate point = Coordinate.GetOrigin();

Console.WriteLine(point.x); //輸出: 0  
Console.WriteLine(point.y); //輸出: 0 

C#枚舉

在 C# 中,枚舉(Enum)是一種用于定義命名整數(shù)常數(shù)集合的數(shù)據(jù)類型。

在 C# 中,枚舉 用于將常量名稱分配給一組數(shù)字整數(shù)值。

它使常量值更具可讀性,例如,當(dāng)引用一周中的某一天時(shí),WeekDays.Monday 比數(shù)字 0 更具可讀性。

以下是一個(gè)簡單的枚舉的定義示例:

public enum DaysOfWeek{
    Sunday,    // 默認(rèn)值為 0
    Monday,    // 默認(rèn)值為 1
    Tuesday,   // 默認(rèn)值為 2
    Wednesday, // 默認(rèn)值為 3
    Thursday,  // 默認(rèn)值為 4
    Friday,    // 默認(rèn)值為 5
    Saturday   // 默認(rèn)值為 6
}

枚舉值

在這個(gè)示例中,DaysOfWeek 枚舉表示一周中的每一天,每個(gè)成員都有一個(gè)與其關(guān)聯(lián)的整數(shù)值,默認(rèn)從 0 開始遞增。如果你不為枚舉成員指定特定的值,它們的值將按照默認(rèn)規(guī)則遞增,從 0 開始。

您可以為枚舉成員分配不同的值。 枚舉成員默認(rèn)值的更改將自動(dòng)按順序?qū)⒃隽恐捣峙浣o其他成員。

enum Categories
{
    Electronics,    // 0
    Food,           // 1
    Automotive = 6, // 6
    Arts,           // 7
    BeautyCare,     // 8
    Fashion         // 9
}

枚舉可以是任何數(shù)字?jǐn)?shù)據(jù)類型,例如 byte、sbyte、short、ushort、int、uint、long 或 ulong。 但是,枚舉不能是字符串類型。

事件

what

當(dāng)一個(gè)對象發(fā)生重要事件時(shí),通常需要通知其他對象來執(zhí)行相應(yīng)的操作。

事件使對象或類具備通知能力的成員,以手機(jī)(對象)舉例:手機(jī)可以響鈴,響鈴(事件)使手機(jī)具備了通知能力。


事件的作用: 事件是類發(fā)送通知或信息到其它類的一種溝通機(jī)制。C# 中使用事件機(jī)制實(shí)現(xiàn)線程間的通信


事件是委托的一個(gè)子集,為了滿足“廣播/訂閱”模式的需求而生。

為觸發(fā)事件(To raise an event),需要一個(gè)事件發(fā)布者,為接收和處理事件,需要一個(gè)訂閱者或多個(gè)訂閱者。

這些通常是由發(fā)布者類和訂閱者類來進(jìn)行實(shí)現(xiàn)。【觀察者設(shè)計(jì)模式】


我們使用事件:

1、解耦我們的應(yīng)用程序,或者說是松耦合;在沒有破壞已有代碼的情況下,松耦合的程序容易擴(kuò)展并達(dá)到我們想要做的;

2、用于對象之間聯(lián)系的運(yùn)行機(jī)制;

3、在不改變已有代碼的情況下,提供一種簡單而有效的方式去擴(kuò)展應(yīng)用程序;

定義

定義一個(gè)事件需要使用event關(guān)鍵字,事件依賴于委托,

故其后是一個(gè)事件依賴的委托類型(可以是通用委托類型,也可以是自定義的委托類型)。

與定義委托一樣,只不過比它多了一個(gè)關(guān)鍵字event。

// define a delegage(聲明一個(gè)事件依賴的委托)
public delegate void FoodPreparedEventHandler(object source, EventArgs args);
// declare the event(定義一個(gè)事件)
//通常,事件的名稱以`On`開頭,但也不是必須的。
public event FoodPreparedEventHandler FoodPrepared;

訂閱/取訂事件

事件以發(fā)布-訂閱模式進(jìn)行工作,那意味著一旦我們訂閱了事件,只有服務(wù)在訂閱就在。

但是有時(shí)候生意邏輯就規(guī)定了可以訂閱也可以取消。比如,用戶可能有權(quán)選擇只接收app通知或只接收郵件通知。

我們要能夠訂閱到我們想要訂閱的事件,可以使用+=運(yùn)算符:

我們要能夠取消訂閱之前訂閱的事件,可以使用-=運(yùn)算符:

//訂閱
orderingService.FoodPrepared += appService.OnFoodPrepared;
orderingService.FoodPrepared += mailService.OnFoodPrepared;
//取消訂閱
orderingService.FoodPrepared -= appService.OnFoodPrepared;
orderingService.FoodPrepared -= mailService.OnFoodPrepared;

委托的綁定解綁VS事件的訂閱/取訂:

  • 委托的綁定和解綁方法可以使用賦值運(yùn)算符=,也有使用+=-=等運(yùn)算符。
  • 事件的訂閱和取訂則只有使用+=-=運(yùn)算符。

事件運(yùn)行的底層原理

當(dāng)不使用event(事件)的應(yīng)用程序:

以一個(gè)食品服務(wù)程序?yàn)槔?/p>

其中的Order類是包含食品類目名稱和成分;

其中的FoodOrderingService類是用于處理食品預(yù)訂的真正服務(wù)類;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;


//正如我們看到的,我們拿到了需要準(zhǔn)備的菜單,模擬4秒鐘的等待時(shí)間以進(jìn)行準(zhǔn)備,然后我們發(fā)送了一條通知到用戶程序:預(yù)訂在準(zhǔn)備。
//當(dāng)然,這僅僅是個(gè)測試所以示例相當(dāng)簡單,我們用console模擬通知消息。在實(shí)際的應(yīng)用中會(huì)包含很多步驟。
namespace Order
{
    class FoodOrderingService
    {	//調(diào)用服務(wù)來購買一個(gè)帶很多奶酪的pizza:
        static void Main(string[] args)
        {
            var order = new Order { Item = "Pizza with extra cheese" };
            var orderingService = new FoodOrderingService();
            orderingService.PrepareOrder(order);
        }
		
        //食品準(zhǔn)備服務(wù)
        public void PrepareOrder(Order order)
        {
            Console.WriteLine("Preparing your order '{0}',please wait ......", order.Item);
            Thread.Sleep(4000);

            AppService _appService = new AppService();
            _appService.SendAppNotification();
        }
    }
    //發(fā)送通知的服務(wù)類
    public class AppService
    {
        public void SendAppNotification()
        {
            Console.WriteLine("AppService:your food is prepared!");
        }
    }
	//食品訂單類
    public class Order
    {
        public string Item { get; set; }
        public string Ingredients{get;set;}
    }
}

但是,我們決定擴(kuò)展程序用email通知用戶他們的訂餐準(zhǔn)備好了。

為達(dá)到上面的目的,需擴(kuò)展service類代碼并修改相關(guān)代碼,結(jié)果如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Order
{
    class FoodOrderingService
    {
        static void Main(string[] args)
        {
            var order = new Order { Item = "Pizza with extra cheese"};
            var orderingService = new FoodOrderingService();
            orderingService.PrepareOrder(order);
            Console.ReadKey();
        }

        public void PrepareOrder(Order order)
        {
            Console.WriteLine("Preparing your order '{0}',please wait ......", order.Item);
            Thread.Sleep(4000);

            AppService _appService = new AppService();
            _appService.SendAppNotification();

            MailService _mailService = new MailService();
            _mailService.SendEmailNotification();
        }
    }
    
    public class AppService
    {
        public void SendAppNotification()
        {
            Console.WriteLine("AppService:your food is prepared!");
        }
    }
    
    public class MailService
    {
        public void SendEmailNotification()
        {
            Console.WriteLine("MailService:your food is prepared.");
        }
    }
	
    public class Order
    {
        public string Item { get; set; }
        public string Ingredients{get;set;}
    }

}

這很容易給我們的程序引入bug,甚至是如果我們寫了個(gè)單元測試,我們可能要重新梳理并修改代碼。

同理,我們在FoodOrderingService類中同時(shí)引入appservice 和 mailservice函數(shù)來發(fā)送通知消息,創(chuàng)建了一個(gè)緊密相聯(lián)的耦合程序。

這種實(shí)現(xiàn)方法不是我們期望的!我們嘗試使用event事件提升一下這個(gè)示例,這是引入發(fā)布-訂閱模式的完美場合.


當(dāng)使用了event(事件)的應(yīng)用程序:

看一下如何運(yùn)用委托和事件用上面的示例來演示event事件。

創(chuàng)建事件發(fā)布者。對于事件發(fā)布者,我們需要做:

1、定義一個(gè)委托;

2、定義一個(gè)依賴委托的事件;

3、觸發(fā)事件;


1.定義一個(gè)事件依賴的委托

  • 事件中定義的委托通常有2個(gè)參數(shù):

    第一個(gè)參數(shù)是事件源(表示觸發(fā)事件的那個(gè)組件 如(button/label/listview…),比如說你單擊button,那么sender就是button),更確切地說是將要發(fā)布事件的那個(gè)類;

    第二個(gè)參數(shù)EventArgs(它用來輔助你處理事件,比如說你用鼠標(biāo)點(diǎn)擊窗體,那么EventArgs是會(huì)包含點(diǎn)擊的位置等等),是與事件相關(guān)的任何其它數(shù)據(jù)。

  • 通常來說,我們會(huì)給委托起一個(gè)描述性的名字,比如“FoodPrepared”,然后在名字末尾添加上“EventHandler”。

    無論委托名稱怎么變化,人們都能很容易了解這是個(gè)委托。

public class FoodOrderingService
{
    // define a delegate(聲明一個(gè)委托)
    //委托FoodPreparedEventHandler返回void
    //事件是FoodPreparedEventHandler類型,因?yàn)槲覀兌x的是一旦操作完成就會(huì)觸發(fā)的事件,所以給它起了個(gè)過去時(shí)的名字---FoodPrepared。
    public delegate void FoodPreparedEventHandler(object source, EventArgs args);

    ...
}

2.再定義一個(gè)依賴委托的事件

定義一個(gè)事件需要使用event關(guān)鍵字,其后是一個(gè)委托類型(可以是通用委托類型,也可以是自定義的委托類型)。

與定義委托一樣,只不過比它多了一個(gè)關(guān)鍵字event。

public class FoodOrderingService
{
    // define a delegage(聲明一個(gè)事件依賴的委托)
    public delegate void FoodPreparedEventHandler(object source, EventArgs args);
    // declare the event(定義一個(gè)事件)
    //通常,事件的名稱以`On`開頭,但也不是必須的。
    public event FoodPreparedEventHandler FoodPrepared;

    ...
}

3.觸發(fā)事件,我們再創(chuàng)建一個(gè)用于觸發(fā)事件的方法函數(shù)

public class FoodOrderingService
{	//delegate
    public delegate void FoodPreparedEventHandler(object source, EventArgs args);
    //event
    public event FoodPreparedEventHandler FoodPrepared;
	//被調(diào)用的函數(shù)
    public void PrepareOrder(Order order)
    {
        Console.WriteLine($"Preparing your order '{order.Item}', please wait...");
        Thread.Sleep(4000);
		//調(diào)用觸發(fā)事件的方法函數(shù)
        OnFoodPrepared();
    }
	//觸發(fā)事件的方法函數(shù)	
    //按照慣例,方法函數(shù)的修飾符應(yīng)該是protected virtual void,名稱前綴加“On”。
    protected virtual void OnFoodPrepared()
    {	
        //觸發(fā)事件(調(diào)用委托(調(diào)用委托綁定的具體方法))
        //函數(shù)體內(nèi)部,我們檢查是否有訂閱者(FoodPrepared != null),如果有訂閱者,我們就調(diào)用事件,將this做為參數(shù)傳遞,this是當(dāng)前類;null做為事件參數(shù)。
        if (FoodPrepared != null)
            FoodPrepared(this, null);
    }
}

創(chuàng)建訂閱者:

1.創(chuàng)建AppService類和MailService

//訂閱者1
public class AppService
{
    //其中的事件處理方法
    public void OnFoodPrepared(object source, EventArgs eventArgs)
    {
        Console.WriteLine("AppService: your food is prepared.");
    }
}
//訂閱者2
public class MailService
{
    //其中的事件處理方法
    public void OnFoodPrepared(object source, EventArgs eventArgs)
    {
        Console.WriteLine("MailService: your food is prepared.");
    }
}

2.現(xiàn)在實(shí)例化AppService類MailService類,并訂閱到FoodPrepared事件。

用 += 運(yùn)算符可以訂閱到事件,在示例中我們將訂閱者AppService類和訂閱者MailService類訂閱到FoodPrepared事件中,用AppService類中的OnFoodPrepared函數(shù)以及MailService類中的OnFoodPrepared函數(shù)來處理事件。

static void Main(string[] args)
{
    //訂單對象初始化
    var order = new Order { Item = "Pizza with extra cheese" };
	//發(fā)布者
    var orderingService = new FoodOrderingService();
    //訂閱者
    var appService = new AppService();
    var mailService = new MailService();
	//訂閱事件
    orderingService.FoodPrepared += appService.OnFoodPrepared;
    orderingService.FoodPrepared += mailService.OnFoodPrepared;
	//調(diào)用對應(yīng)方法,其中觸發(fā)了事件
    orderingService.PrepareOrder(order);
	
    Console.ReadKey();
}

3.故完整代碼如下:

1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 
 7 namespace Order
 8 {
       //client
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             var order = new Order { Item = "Pizza with extra cheese" };
14             var orderingService = new FoodOrderingService();
15             var appService = new AppService();
16             var mailService = new MailService();
17 
18             orderingService.FoodPrepared += appService.OnFoodPrepared;
19             orderingService.FoodPrepared += mailService.OnFoodPrepared;
20 
21             orderingService.PrepareOrder(order);
22             Console.ReadKey();
23         } 
24     }
25 		//發(fā)布者
26     class FoodOrderingService
27     {
28         //定義委托
29         public delegate void FoodPreparedEventHandler(object source, EventArgs args);
30         //聲明事件
31         public event FoodPreparedEventHandler FoodPrepared;
32 
33         public void PrepareOrder(Order order)
34         {
35 
36             Console.WriteLine("Preparing your order '{0}',please wait ......", order.Item);
37             Thread.Sleep(4000);
38 				//調(diào)用觸發(fā)事件的方法
39             OnFoodPrepared();
40         }
41         //觸發(fā)事件的函數(shù)方法
42         protected virtual void OnFoodPrepared()
43         {
44             if (FoodPrepared != null)
45             {
46                 FoodPrepared(this,null);
47             }
48         }
49     }
       //訂閱者
50     public class AppService
51     {
52         public void OnFoodPrepared(object source ,EventArgs eventArgs)
53         {
54             Console.WriteLine("AppService:your food is prepared!");
55         }
56     }
     	//訂閱者
57     public class MailService
58     {
59         public void OnFoodPrepared(object sorece , EventArgs eventArgs)
60         {
61             Console.WriteLine("MailService:your food is prepared.");
62         }
63     }
64 	   //訂單類
65     public class Order
66     {
67         public string Item { get; set; }
68         public string Ingredients{get;set;}
69     }
70 
71     
72 }

可以像這樣無限地?cái)U(kuò)展我們的程序,我們也可以將FoodOrderingService類移到其它類庫中或我們想要的地方。

擴(kuò)展EventArgs參數(shù)

像前面提到的那樣,之前我們用EventArgs發(fā)送事件數(shù)據(jù)。

但是其實(shí)我們能創(chuàng)建自EventArgs繼承的自定義事件參數(shù)類:FoodPreparedEventArgs,自定義格式內(nèi)容,從而用于發(fā)送數(shù)據(jù)到訂閱者。

//擴(kuò)展和繼承自EventArgs的自定義事件參數(shù)類:FoodPreparedEventArgs
1 public class FoodPreparedEventArgs : EventArgs
2 {
     //自定義字段,其是發(fā)送給訂閱者的數(shù)據(jù)
3     public Order Order { get; set; }
4 }

然后,我們修改發(fā)布者FoodOrderingService中的事件參數(shù)類型:

//現(xiàn)在我們正發(fā)送order數(shù)據(jù)(OnFoodPrepared(order);)到訂閱者。
//用FoodPreparedEventArgs代替了EventArgs并將order信息傳遞給了訂閱者。
public class FoodOrderingService
{
    //事件參數(shù)替換為:FoodPreparedEventArgs
    public delegate void FoodPreparedEventHandler(object source, FoodPreparedEventArgs args);
    public event FoodPreparedEventHandler FoodPrepared;

    public void PrepareOrder(Order order)
    {
        Console.WriteLine($"Preparing your order '{order.Item}', please wait...");
        Thread.Sleep(4000);

        OnFoodPrepared(order);
    }

    protected virtual void OnFoodPrepared(Order order)
    {
        if (FoodPrepared != null)
            FoodPrepared(this, new FoodPreparedEventArgs { Order = order });
    }
}

修改訂閱者中的事件參數(shù)類型:

//AppService類
public class AppService
{
    public void OnFoodPrepared(object source ,FoodPreparedEventArgs eventArgs)
    {
         Console.WriteLine($"AppService: your food '{eventArgs.Order.Item}' is prepared.");
    }
}
    
//MailService類
public class MailService
{
    public void OnFoodPrepared(object sorece , FoodPreparedEventArgs eventArgs)
    {
        Console.WriteLine($"MailService: your food '{eventArgs.Order.Item}' is prepared.");
    }
}

完整代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Order
{
    class Program
    {
        static void Main(string[] args)
        {
            var order = new Order { Item = "Pizza with extra cheese" };
            var orderingService = new FoodOrderingService();
            var appService = new AppService();
            var mailService = new MailService();

            orderingService.FoodPrepared += appService.OnFoodPrepared;
            orderingService.FoodPrepared += mailService.OnFoodPrepared;

            orderingService.PrepareOrder(order);
            Console.ReadKey();
        }
    }

    class FoodOrderingService
    {
        //定義委托
        public delegate void FoodPreparedEventHandler(object source, FoodPreparedEventArgs args);
        //聲明事件
        public event FoodPreparedEventHandler FoodPrepared;

        public void PrepareOrder(Order order)
        {

            Console.WriteLine("Preparing your order '{0}',please wait ......", order.Item);
            Thread.Sleep(4000);
			//觸發(fā)事件
            OnFoodPrepared(order);
        }
        //觸發(fā)事件的函數(shù)方法
        protected virtual void OnFoodPrepared(Order order)
        {
            if (FoodPrepared != null)
            {
                FoodPrepared(this, new FoodPreparedEventArgs { Order = order});
            }
        }
    }
    public class AppService
    {
        public void OnFoodPrepared(object source, FoodPreparedEventArgs eventArgs)
        {
            Console.WriteLine("AppService:your food {0} is prepared!",eventArgs.Order.Item);
        }
    }
    public class MailService
    {
        public void OnFoodPrepared(object sorece , FoodPreparedEventArgs eventArgs)
        {
            Console.WriteLine("MailService:your food {0} is prepared!", eventArgs.Order.Item);
        }
    }

    public class Order
    {
        public string Item { get; set; }
        public string Ingredients{get;set;}
    }
	//自定義的事件參數(shù)類
    public class FoodPreparedEventArgs : EventArgs
    {
        public Order Order { get; set; }
    }
}

用EventHandler類來簡化事件使用

現(xiàn)在,通過**.net**用一些適合我們的現(xiàn)代方式修改代碼,在C#中使用event handler。

  • 主要使用EventHandlerEventHandler<TEventArgs>創(chuàng)建事件,他們是專門的封裝器,可以簡化事件的創(chuàng)建。

EventHandler類似于提供了的已經(jīng)封裝好了的事件依賴的委托類型。

EventHandler<TEventArgs>類<>的TEventArgs代表自定義的事件相關(guān)參數(shù)類型。

  • 使用了傳統(tǒng)的Invoke()函數(shù)來觸發(fā)事件,避免復(fù)雜的null檢查,可以清理項(xiàng)目冗余代碼。

所以現(xiàn)在我們通過添加event handler修改上面的FoodOrderingService類:

public class FoodOrderingService
{
    //使用EventHandler類型創(chuàng)建事件(委托)
    //現(xiàn)在,用于代替委托和事件的兩種聲明,我們用EventHandler和FoodPreparedEventArgs,這樣可以使代碼更簡潔,更有可讀性。      //這可能是你在其它項(xiàng)目中使用事件時(shí)會(huì)看到的。
    public event EventHandler<FoodPreparedEventArgs> FoodPrepared;

    public void PrepareOrder(Order order)
    {
        Console.WriteLine($"Preparing your order '{order.Item}', please wait...");
        Thread.Sleep(4000);

        OnFoodPrepared(order);
    }
	
    protected virtual void OnFoodPrepared(Order order)
    {
        //使用了傳統(tǒng)的Invoke()函數(shù)來觸發(fā)事件,避免復(fù)雜的null檢查,可以清理項(xiàng)目冗余代碼。
        FoodPrepared?.Invoke(this, new FoodPreparedEventArgs { Order = order });
    }
}

c#流(stream)

C# 包括以下標(biāo)準(zhǔn) IO(輸入/輸出)類,用于從文件、內(nèi)存、網(wǎng)絡(luò)、隔離存儲(chǔ)等不同來源讀取/寫入。

字節(jié)流

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

Stream:System.IO.Stream 是一個(gè)抽象類,它提供了將字節(jié)(讀取、寫入等)傳輸?shù)皆吹臉?biāo)準(zhǔn)方法。它就像一個(gè)包裝類來傳輸字節(jié)。需要從特定源讀取/寫入字節(jié)的類必須實(shí)現(xiàn) Stream 類。


以下字節(jié)類繼承 Stream 類以提供從特定源讀取/寫入字節(jié)的功能:

FileStream: 從/向物理文件讀取或?qū)懭胱止?jié),無論它是 .txt、.exe、.jpg 還是任何其他文件。 FileStream 派生自 Stream 類。

MemoryStream:MemoryStream 讀取或?qū)懭氪鎯?chǔ)在內(nèi)存中的字節(jié)。

BufferedStream:BufferedStream 從其他流讀取或?qū)懭胱止?jié)以提高某些 I/O 操作的性能。

NetworkStream:NetworkStream 從網(wǎng)絡(luò)套接字讀取或?qū)懭胱止?jié)。

PipeStream:PipeStream 從不同進(jìn)程讀取或?qū)懭胱止?jié)。

CryptoStream:CryptoStream 用于將數(shù)據(jù)流鏈接到加密轉(zhuǎn)換。


字符流

StreamReader:StreamReader 是一個(gè)輔助類,用于通過使用編碼值將字節(jié)轉(zhuǎn)換為字符來從 Stream 中讀取字符。 它可用于從不同的流(如 FileStream、MemoryStream 等)讀取字符串(字符)。

StreamWriter:StreamWriter 是一個(gè)幫助類,用于通過將字符轉(zhuǎn)換為字節(jié)來將字符串寫入 Stream。 它可用于將字符串寫入不同的流,例如 FileStream、MemoryStream 等。

BinaryReader:BinaryReader 是一個(gè)幫助類,用于從字節(jié)讀取原始數(shù)據(jù)類型。

BinaryWriter:BinaryWriter 以二進(jìn)制形式寫入原始類型。

舉個(gè)例子:
#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

上圖顯示 FileStream 從物理文件中讀取字節(jié),然后 StreamReader 通過將這些字節(jié)轉(zhuǎn)換為字符串來讀取字符串。 同理,StreamWriter 將字符串轉(zhuǎn)換為字節(jié)寫入 FileStream,然后 FileStream 將字節(jié)寫入物理文件。

因此,F(xiàn)ileStream 處理字節(jié),而 StreamReader 和 StreamWriter 處理字符串。


C# 文件操作

C# 提供了下面的這些類用來操作文件,它們可以用來訪問文件夾,文件,打開文件,寫入文件,創(chuàng)建文件, 移動(dòng)文件,列出目錄下所有的文件等等。

類名 用法
File File 是一個(gè)靜態(tài)類,提供各種功能,如復(fù)制、創(chuàng)建、移動(dòng)、刪除、打開以讀取或/寫入、加密或解密、檢查文件是否存在、添加新的文件內(nèi)容、獲取最后訪問權(quán)限 時(shí)間等
FileInfo FileInfo 類提供與靜態(tài) File 類相同的功能。 通過手動(dòng)編寫代碼來讀取或?qū)懭胛募械淖止?jié),您可以更好地控制如何對文件進(jìn)行讀/寫操作。
Directory Directory 是一個(gè)靜態(tài)類,提供創(chuàng)建、移動(dòng)、刪除和訪問子目錄的功能。
DirectoryInfo DirectoryInfo 提供了創(chuàng)建、移動(dòng)、刪除和訪問子目錄的實(shí)例方法。
Path Path 是一個(gè)靜態(tài)類,它提供檢索文件擴(kuò)展名、更改文件擴(kuò)展名、檢索絕對物理路徑以及其他與路徑相關(guān)的功能等功能。

File類

靜態(tài) File 類包括各種實(shí)用方法來與任何類型的物理文件進(jìn)行交互,例如二進(jìn)制,文本等

使用這個(gè)靜態(tài) File 類對物理文件執(zhí)行一些快速操作。

File是在System.IO下面的,所以我們得引用這個(gè)命名空間:

using System.IO;

File 的重要方法:

方法 用法
AppendAllLines 在文件中追加行,然后關(guān)閉文件。如果指定的文件不存在,此方法會(huì)創(chuàng)建一個(gè)文件,將指定的行寫入文件,然后關(guān)閉文件。
AppendAllText 打開文件,將指定的字符串附加到文件,然后關(guān)閉文件。如果文件不存在,此方法會(huì)創(chuàng)建一個(gè)文件,將指定的字符串寫入文件,然后關(guān)閉文件。
AppendText 創(chuàng)建一個(gè) StreamWriter,它將 UTF-8 編碼的文本附加到現(xiàn)有文件,如果指定的文件不存在,則附加到新文件。
Copy 將現(xiàn)有文件復(fù)制到新文件。不允許覆蓋同名文件。
Create 創(chuàng)建或覆蓋指定路徑中的文件。 返回一個(gè)FileStream
CreateText 創(chuàng)建或打開用于寫入 UTF-8 編碼文本的文件。 返回一個(gè)StreamWriter
Decrypt 解密當(dāng)前帳戶使用加密方法加密的文件。 (依賴于CSP)
Delete 刪除指定的文件。
Encrypt 加密文件,以便只有用于加密文件的帳戶才能對其進(jìn)行解密。 (依賴于CSP)
Exists 確定指定的文件是否存在。
Move 將指定文件移動(dòng)到新位置,提供指定新文件名的選項(xiàng)。
Open 在指定路徑上打開一個(gè)具有讀/寫訪問權(quán)限的 FileStream。
ReadAllBytes 打開二進(jìn)制文件,將文件內(nèi)容讀入字節(jié)數(shù)組,然后關(guān)閉文件。
ReadAllLines 打開一個(gè)文本文件,讀取文件的所有行,然后關(guān)閉文件。
ReadAllText 打開一個(gè)文本文件,讀取文件的所有行,然后關(guān)閉文件。
Replace 用另一個(gè)文件的內(nèi)容替換指定文件的內(nèi)容,刪除原始文件,并創(chuàng)建替換文件的備份。
WriteAllBytes 創(chuàng)建一個(gè)新文件,將指定的字節(jié)數(shù)組寫入文件,然后關(guān)閉文件。如果目標(biāo)文件已經(jīng)存在,它會(huì)被覆蓋。
WriteAllLines 創(chuàng)建一個(gè)新文件,將一組字符串寫入該文件,然后關(guān)閉該文件。
WriteAllText 創(chuàng)建一個(gè)新文件,將指定的字符串寫入文件,然后關(guān)閉文件。如果目標(biāo)文件已經(jīng)存在,它會(huì)被覆蓋。

FileInfo類

FileInfo 類提供與靜態(tài) File 類相同的功能,但您可以通過手動(dòng)編寫代碼來從文件讀取或?qū)懭胱止?jié),從而更好地控制文件的讀/寫操作。

其重要屬性和方法:

屬性 說明
Directory 獲取父目錄的實(shí)例。
DirectoryName 獲取表示目錄完整路徑的字符串。
Exists 獲取一個(gè)值,該值指示文件是否存在。
Extension 獲取表示文件擴(kuò)展名部分的字符串。
FullName 獲取目錄或文件的完整路徑。
IsReadOnly 獲取或設(shè)置一個(gè)值,該值確定當(dāng)前文件是否為只讀。
LastAccessTime 獲取或設(shè)置上次訪問當(dāng)前文件或目錄的時(shí)間
LastWriteTime 獲取或設(shè)置當(dāng)前文件或目錄上次寫入的時(shí)間
Length 獲取當(dāng)前文件的大小(以字節(jié)為單位)。
Name 獲取文件的名稱。
方法 說明
AppendText 創(chuàng)建一個(gè) StreamWriter,它將文本附加到由 FileInfo 的此實(shí)例表示的文件。
CopyTo 將現(xiàn)有文件復(fù)制到新文件,不允許覆蓋現(xiàn)有文件。
Create 創(chuàng)建文件。 返回 FileStream
CreateText 創(chuàng)建一個(gè)寫入新文本文件的 StreamWriter。
Decrypt 解密當(dāng)前帳戶使用加密方法加密的文件。 (依賴于CSP)
Delete 刪除指定的文件。
Encrypt 加密文件,以便只有用于加密文件的帳戶才能對其進(jìn)行解密。 (依賴于CSP)
MoveTo 將指定文件移動(dòng)到新位置,提供指定新文件名的選項(xiàng)。
Open 在指定的 FileMode 中打開一個(gè)。
OpenRead 創(chuàng)建一個(gè)只讀的 FileStream。
OpenText 使用 UTF8 編碼創(chuàng)建一個(gè)從現(xiàn)有文本文件讀取的 StreamReader。
OpenWrite 創(chuàng)建一個(gè)只寫的 FileStream。
Replace 用當(dāng)前 FileInfo 對象描述的文件替換指定文件的內(nèi)容,刪除原始文件,并創(chuàng)建替換文件的備份。
ToString 以字符串形式返回路徑。

Directory類

操作文件夾的一個(gè)靜態(tài)類。

下面是其常用的一些方法:

說明
CreateDirectory(String) 在指定路徑中創(chuàng)建所有目錄和子目錄,除非它們已經(jīng)存在。
Delete(String) 從指定路徑刪除空目錄。
Delete(String, Boolean) 刪除指定的目錄,并刪除該目錄中的所有子目錄和文件(如果表示)。
Exists(String) 確定給定路徑是否引用磁盤上的現(xiàn)有目錄。
GetCurrentDirectory() 獲取應(yīng)用程序的當(dāng)前工作目錄。
GetDirectories(String) 返回指定目錄中的子目錄的名稱(包括其路徑)。
GetDirectories(String, String) 返回指定目錄中與指定的搜索模式匹配的子目錄的名稱(包括其路徑)。
GetDirectories(String, String, EnumerationOptions) 返回指定目錄中與指定的搜索模式和枚舉選項(xiàng)匹配的子目錄的名稱(包括其路徑)。
GetDirectories(String, String, SearchOption) 返回與在指定目錄中的指定搜索模式匹配的子目錄的名稱(包括其路徑),還可以選擇地搜索子目錄。
GetFiles(String) 返回指定目錄中文件的名稱(包括其路徑)
GetFiles(String, String) 返回指定目錄中與指定的搜索模式匹配的文件的名稱(包含其路徑)。
GetFiles(String, String, EnumerationOptions) 返回指定目錄中與指定的搜索模式和枚舉選項(xiàng)匹配的文件的名稱(包括其路徑)
GetFiles(String, String, SearchOption) 返回指定目錄中與指定的搜索模式匹配的文件的名稱(包含其路徑),使用某個(gè)值確定是否要搜索子目錄。
GetFileSystemEntries(String) 返回指定路徑中的所有文件和子目錄的名稱。
GetFileSystemEntries(String, String) 返回一個(gè)數(shù)組,其中包含與指定路徑中的搜索模式相匹配的文件名和目錄名稱。
GetFileSystemEntries(String, String, EnumerationOptions) 返回指定路徑中與搜索模式和枚舉選項(xiàng)匹配的文件名和目錄名的數(shù)組。
GetFileSystemEntries(String, String, SearchOption) 返回指定路徑中與搜索模式匹配的所有文件名和目錄名的數(shù)組,還可以搜索子目錄。
Move(String, String) 將文件或目錄及其內(nèi)容移到新位置。

DirectoryInfo類

主要用來獲取當(dāng)前的目錄名字為主,因?yàn)槠涓鶧irectoy都是類似的

class Program
{
    static async Task Main(string[] args)
    {
        var info = new DirectoryInfo(@"D:\GitHub\Articles\csharp");
        Console.WriteLine(info.Name);
    }
}

Path類

合并路徑

static async Task Main(string[] args)
{
    var path = Path.Combine("abc", "dd");
    Console.WriteLine(path);
}
//windows下 輸出 abc\dd linux 下輸出的是 abc/dd

得到擴(kuò)展名

static async Task Main(string[] args)
{
    var extension = Path.GetExtension("asdf/sdfsdf/sdf.png");
    Console.WriteLine(extension);
}

得到文件名

static async Task Main(string[] args)
{
    var fileName = Path.GetFileName("d:/as/aa/abc.txt");
    Console.WriteLine(fileName);//abc.txt

    var fileNameWithoutExtension = Path.GetFileNameWithoutExtension("d:/as/aa/abc.txt");
    Console.WriteLine(fileNameWithoutExtension);//abc
}

創(chuàng)建臨時(shí)文件

static async Task Main(string[] args)
{
    var tempFile = Path.GetTempFileName();
    Console.WriteLine(tempFile);
}

異步編程

異步與多線程的關(guān)系

異步是一種編程模型,目的是在等待 I/O 操作時(shí)不阻塞調(diào)用io操作的線程,以提高程序的效率和響應(yīng)性。

多線程是一種并發(fā)編程模型,通過同時(shí)運(yùn)行多個(gè)線程來提高性能,但需要注意線程同步和共享資源的問題,避免競態(tài)條件和死鎖。

綜合考慮:

  • 在 I/O 密集型任務(wù)中,異步編程通常更適用,因?yàn)樗軌蚋行У乩觅Y源,提高響應(yīng)性。
  • 在 CPU 密集型任務(wù)中,多線程可能更合適,因?yàn)樗梢栽诙鄠€(gè)核上并行執(zhí)行任務(wù),提高計(jì)算性能。

總的來說,選擇異步還是多線程取決于具體的應(yīng)用場景和任務(wù)類型。在現(xiàn)代編程中,往往會(huì)將異步編程和多線程結(jié)合使用,以充分發(fā)揮它們各自的優(yōu)勢。

Task類

概述:

  • Task 是用于表示一個(gè)異步操作的類,通常用于沒有返回值的異步方法。
  • 可以通過 Task.RunTask.Factory.StartNew 創(chuàng)建 Task 對象,也可以在異步方法中直接返回 Task。
  • 選擇異步方法的返回類型取決于你的需求和應(yīng)用場景。通常情況下,推薦使用 TaskTask<TResult>,因?yàn)樗鼈兲峁┝艘环N通用的方式來表示異步操作,并且能夠充分利用異步編程的優(yōu)勢。

創(chuàng)建 Task 對象:

// 使用 Task.Run,通常情況下,推薦使用 Task.Run,因?yàn)樗峁┝艘环N簡潔且性能良好的方式來在新的任務(wù)上執(zhí)行操作。
Task task1 = Task.Run(() => SomeMethod());

// 使用 Task.Factory.StartNew
Task task2 = Task.Factory.StartNew(() => SomeMethod(),
                                    CancellationToken.None,
                                    TaskCreationOptions.DenyChildAttach,
                                    TaskScheduler.Default);

創(chuàng)建Task對象時(shí)Task.Run 和 Task.Factory.StartNew 之間的區(qū)別是什么?

  1. 默認(rèn)調(diào)度器
    • Task.Run 使用 TaskScheduler.Default,它是線程池調(diào)度器,通常用于執(zhí)行計(jì)算密集型的任務(wù)。
    • Task.Factory.StartNew 默認(rèn)使用 TaskScheduler.Current,它繼承自調(diào)用線程的任務(wù)調(diào)度器,適用于 UI 線程或其他自定義調(diào)度器。
  2. 異常處理
    • Task.Run 更加友好地處理異常,不會(huì)封裝在 AggregateException 中。
    • Task.Factory.StartNew 在沒有明確指定選項(xiàng)的情況下,可能會(huì)將異常封裝在 AggregateException 中。
  3. 默認(rèn)選項(xiàng)
    • Task.Run 使用一組默認(rèn)選項(xiàng),適用于大多數(shù)常見的情況。
    • Task.Factory.StartNew 需要手動(dòng)配置選項(xiàng),例如 TaskCreationOptionsTaskScheduler。

異步方法返回 Task:

public async Task DoAsyncOperation()
{
    // 異步操作
}

等待 Task 完成:

Task myTask = DoAsyncOperation();
await myTask;

使用 Task.ContinueWith 處理完成后的任務(wù):

Task myTask = Task.Run(() => SomeMethod());
myTask.ContinueWith(task =>  Console.WriteLine("Task Completed"));

Task返回異常信息

在異步方法中,如果 Task 返回了異常,可以通過檢查 Task.Exception 屬性來獲取異常信息。

Task myTask = Task.Run(() => SomeMethod());
Console.WriteLine(myTask.Exception);

Task<TResult >類

概述:

  • Task<TResult>Task 的泛型版本,用于表示一個(gè)異步操作,其中包含返回值。
  • 通過 Task.RunTask.Factory.StartNew 創(chuàng)建 Task 對象,或者在異步方法中直接返回 Task<TResult>。

創(chuàng)建 Task 對象:

Task<int> myTask = Task.Run(() => SomeMethodReturningInt());

異步方法返回 Task:

public async Task<int> GetNumberAsync()
{
    // 異步操作,并返回一個(gè)整數(shù)結(jié)果
    return await SomeAsyncNumberOperation();
}

等待 Task 完成并獲取結(jié)果:

Task<int> myTask = GetNumberAsync();
int result = await myTask;

async與await關(guān)鍵字

在async修飾的方法中,總會(huì)出現(xiàn)await的身影。所以你想拋開async和await中的某一個(gè),去單獨(dú)認(rèn)識(shí)另一個(gè)是很難的。

async概述

async是一個(gè)關(guān)鍵字,同時(shí)也是修飾符(和abstract、static一樣)。
使用async修飾符可以將一個(gè)方法、lambda表達(dá)式或匿名方法指定為異步的。

如果在方法或表達(dá)式上使用async修飾符,則它就被稱為異步方法(async method)。

下面示例代碼定義了一個(gè)名為ExampleMethodAsync的異步方法:

public async Task<int> ExampleMethodAsync(){
    //...
}

異步方法的返回類型:

具體取決于異步操作是否有返回值:

  • 異步方法的返回類型可以是 Task,表示異步操作完成,沒有返回值。
  • 也可以是 Task<TResult>,表示異步操作完成,并返回一個(gè)類型為 TResult 的結(jié)果。

await概述

await是一個(gè)operator(運(yùn)算符或者操作符)。

該運(yùn)算符會(huì)掛起(suspend)封閉的異步方法(async method),直到操作對象的異步操作完成。

這句話初看比較難懂,稍微拆解一下。

  • operator,運(yùn)算符,跟加減乘除一樣,作用于某個(gè)值(或?qū)ο螅缓笤撝禃?huì)進(jìn)行一些運(yùn)算,發(fā)生變化。
  • 掛起,就是使某個(gè)過程暫停。
  • 封閉的,我們可以想象方法(或者說函數(shù))是一個(gè)容器,里面裝載了一些運(yùn)算的語句,隨著運(yùn)算的進(jìn)行,方法(容器)中的狀態(tài)會(huì)發(fā)生變化,此時(shí)我掛起方法,就相當(dāng)于把方法(連同那些狀態(tài))封閉起來,不再改變。
  • 異步方法,指的是該方法不是阻塞的,我運(yùn)行到某個(gè)點(diǎn),可能要等很久,此時(shí)我不等了,直接去干別的事情了,該點(diǎn)運(yùn)行完之后通知我回來繼續(xù)運(yùn)行。
  • 直到操作對象的異步操作完成,就是說await作用的對象的其他異步操作還在進(jìn)行,進(jìn)行完了我再回來繼續(xù)執(zhí)行await下面的語句。

在異步編程中,“掛起” 通常指的是在異步操作中暫停當(dāng)前方法的執(zhí)行,以等待某些異步操作的完成。

await 表示在執(zhí)行異步操作時(shí),將控制權(quán)返回給調(diào)用方,直到異步操作完成后再繼續(xù)執(zhí)行。

//下面是一個(gè)簡單的示例,用于理解方法的掛起:
using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Before DoAsync");

        // 調(diào)用異步方法 DoAsync
        await DoAsync();

        Console.WriteLine("After DoAsync");
    }

    static async Task DoAsync()
    {
        Console.WriteLine("Start DoAsync");

        // 模擬一個(gè)異步操作,比如網(wǎng)絡(luò)請求或I/O操作
        await Task.Delay(2000);

        Console.WriteLine("End DoAsync");
    }
}

//在這個(gè)例子中,Main 方法調(diào)用了異步方法 DoAsync。在 DoAsync 方法內(nèi)部,使用了 await Task.Delay(2000) 來模擬一個(gè)異步操作,此時(shí)控制權(quán)會(huì)在異步操作執(zhí)行期間返回給調(diào)用方(Main 方法),這時(shí) Main 方法的執(zhí)行并沒有被阻塞,而是可以繼續(xù)執(zhí)行其他操作。當(dāng)異步操作完成后,DoAsync 方法會(huì)在 await 處恢復(fù)執(zhí)行,之后的代碼才會(huì)執(zhí)行。

//總體來說,異步方法的 "掛起" 是指在遇到異步操作時(shí),暫停當(dāng)前方法的執(zhí)行,將控制權(quán)返回給調(diào)用方,等待異步操作完成后再繼續(xù)執(zhí)行。這使得應(yīng)用程序在執(zhí)行異步操作的同時(shí)可以繼續(xù)執(zhí)行其他任務(wù),提高了程序的響應(yīng)性。

畫個(gè)草圖,理解await運(yùn)算符

藍(lán)色手描實(shí)線表示該方法正在執(zhí)行。
異步≠多線程,但異步往往會(huì)和多線程一起用。

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  • 只能在async關(guān)鍵字修飾的方法、lambda表達(dá)式或匿名方法上使用await運(yùn)算符。

  • await 關(guān)鍵字用于等待異步操作的完成。它會(huì)暫時(shí)將控制權(quán)返回給調(diào)用方,允許調(diào)用方在等待的過程中執(zhí)行其他操作。

  • 當(dāng)遇到 await 表達(dá)式時(shí),當(dāng)前方法會(huì)在此處分割,將剩余部分作為回調(diào)(或稱為后續(xù)任務(wù))注冊到異步操作的完成事件上。


async代碼示例:

在對await有個(gè)大概理解后,繼續(xù)學(xué)習(xí)async關(guān)鍵字

假設(shè)下面代碼是在一個(gè)異步方法中的,并且它調(diào)用了 HttpClient.GetStringAsync方法:

string contents = await httpClient.GetStringAsync(requestUrl);

異步方法會(huì)以同步的方式運(yùn)行,直到遇到await表達(dá)式,此時(shí)該方法被掛起(await表達(dá)式下面的語句停止執(zhí)行),直到等待的任務(wù)完成。

與此同時(shí),控制(執(zhí)行)權(quán)返回給方法的調(diào)用者。

如果async關(guān)鍵字修飾的方法不包含await表達(dá)式或語句,則該方法將同步執(zhí)行。編譯器會(huì)警告你該異步方法不包含await語句,因?yàn)檫@種情況可能會(huì)指示錯(cuò)誤。


總結(jié)一下:

1??async和await往往一起出現(xiàn).

2??async修飾符,指明該方法是一個(gè)異步方法,異步方法應(yīng)該使用 asyncawait 關(guān)鍵字。這樣可以使異步代碼更加清晰。

3??await運(yùn)算符,等候操作對象的異步操作完成


正確使用 async 和 await 的步驟:

  1. 方法聲明: 使用 async 關(guān)鍵字來修飾異步方法。
  2. 返回類型: 異步方法的返回類型應(yīng)該是 TaskTask<TResult>,取決于是否有返回值。
  3. await 關(guān)鍵字: 在異步方法中使用 await 關(guān)鍵字等待異步操作的完成。
  4. 異常處理: 使用 try-catch 塊捕獲異步方法中可能拋出的異常。

異步操作

what is 異步操作?

異步操作是一種允許程序繼續(xù)執(zhí)行其他任務(wù)而不必等待某個(gè)長時(shí)間運(yùn)行的操作完成的編程模型。在異步操作中,程序可以啟動(dòng)一個(gè)任務(wù),然后繼續(xù)執(zhí)行其他任務(wù),而無需等待啟動(dòng)的任務(wù)完成。這對于執(zhí)行可能涉及網(wǎng)絡(luò)請求、文件 I/O、長時(shí)間計(jì)算等的操作特別有用,因?yàn)檫@些操作可能需要一定的時(shí)間來完成。

異步操作的主要優(yōu)勢在于提高程序的響應(yīng)性和效率,特別是在處理用戶界面(UI)或執(zhí)行多個(gè)操作的情況下。而在同步操作中,如果一個(gè)操作耗時(shí)較長,程序可能會(huì)在等待這個(gè)操作完成時(shí)變得不響應(yīng)。

在C#中,異步操作通常使用 asyncawait 關(guān)鍵字,這使得異步編程變得更加清晰和易于理解。

更加具體的來說,異步操作指的是的async異步方法中加了await關(guān)鍵字的相關(guān)表達(dá)式

如何取消異步操作
取消異步操作通常使用 CancellationTokenCancellationTokenSource 來實(shí)現(xiàn)。

1.使用CancellationToken參數(shù):

首先,在異步方法中使用 CancellationToken 參數(shù),例如:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Test{
    public class AsyncCancellationExample{
        public async Task SomeAsyncOperation(CancellartionToken cancellationToken){
            Console.WriteLine("Async operation started.");
            try{
                //模擬異步操作
                await Task.Delay(5000,cancellationToken);
                
                //在異步操作中檢查CancellationToken是否已取消
                cancellationToken.ThrowIfCancellationRequested();
                
                Console.WriteLine("Async operation completed.");
            }catch(OperationCanceledException){
                 Console.WriteLine("Async operation canceled.");
            }
            
        }
    }
    
    public void CancelAsyncOperation(){
        //在需要取消異步操作的地方調(diào)用Cancel方法
        cts?.Cancel();
    }
}

2.使用CancellationTokenSource參數(shù):

在調(diào)用異步方法的地方,創(chuàng)建一個(gè) CancellationTokenSource 對象,并將其 Token 屬性傳遞給異步方法。

在需要取消異步操作的地方,調(diào)用 CancellationTokenSourceCancel 方法。

在這里,StartAsyncOperation 方法中使用 using 語句創(chuàng)建一個(gè)新的 CancellationTokenSource 對象,確保每次開始新的異步操作時(shí)都使用一個(gè)新的 CancellationTokenSource

using System;
using System.Threading;
using System.Threading.Tasks;

public class AsyncCancellationExample
{
    private CancellationTokenSource cts;
    
    public async Task StartAsyncOperation(){
        try{
            // 創(chuàng)建一個(gè)新的異步操作的 CancellationTokenSource
            using(cts = new CancellationTokenSource()){
                //調(diào)用異步方法,并傳遞CancellationTokenSource的Token屬性給異步方法
                await SomeAsyncOperation(cts.Token);
            }
            Console.WriteLine("Async operation completed successfully.");
        }catch (OperationCanceledException)
        {
            Console.WriteLine("Async operation canceled.");
        }
    }
}

c# 異常

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

Exception 類是 SystemException 和 ApplicationException 類的基類。

SystemException 類是程序執(zhí)行過程中可能發(fā)生的所有異常的基類。

如果我們程序希望定義異常的話可以繼承于Exception.

下面列出了一些派生自 System.SystemException 類的預(yù)定義的異常類:

異常類 描述
System.IO.IOException 處理 I/O 錯(cuò)誤。
System.IndexOutOfRangeException 處理當(dāng)方法指向超出范圍的數(shù)組索引時(shí)生成的錯(cuò)誤。
System.ArrayTypeMismatchException 處理當(dāng)數(shù)組類型不匹配時(shí)生成的錯(cuò)誤。
System.NullReferenceException 處理當(dāng)引用一個(gè)空對象時(shí)生成的錯(cuò)誤。
System.DivideByZeroException 處理當(dāng)除以零時(shí)生成的錯(cuò)誤。
System.InvalidCastException 處理在類型轉(zhuǎn)換期間生成的錯(cuò)誤。
System.OutOfMemoryException 處理空閑內(nèi)存不足生成的錯(cuò)誤。
System.StackOverflowException 處理?xiàng)R绯錾傻腻e(cuò)誤。
ArgumentNullException 參數(shù)不應(yīng)為null時(shí)

自定義異常

自定義異常類通常繼承自 Exception 類或其派生類。

自定義異常類的一般步驟:

  1. 創(chuàng)建自定義異常類: 創(chuàng)建一個(gè)繼承自 Exception 類的新類,添加構(gòu)造函數(shù)以及其他需要的屬性和方法。
  2. 使用自定義異常: 在你的代碼中,當(dāng)發(fā)生特定的錯(cuò)誤條件時(shí),拋出自定義異常。

下面是一個(gè)簡單的示例,演示如何創(chuàng)建和使用自定義異常:

using System;

//步驟1:創(chuàng)建自定義異常類
public class CustomException:Exception{
    //添加自定義的屬性,可根據(jù)需要擴(kuò)展
    public int ErrorCode{get;}
    
    //構(gòu)造函數(shù),可根據(jù)需要接受不同的參數(shù)
    public CustomException(int errorCode,string message):base(message){
        ErrorCode = errorCode;
    }
}

//【client】
class Program{
    static void Main(){
        try{
            //步驟2:使用自定義異常
            throw new CustomException(404,"Resource not found");
        }catch(Exception ex){
            Console.WriteLine($"An unexpected error occurred:{ex.Message}");
        }
    }
}

異常處理

異常是在程序執(zhí)行期間出現(xiàn)的問題。異常提供了一種把程序控制權(quán)從某個(gè)部分轉(zhuǎn)移到另一個(gè)部分的方式。

C# 異常處理時(shí)建立在四個(gè)關(guān)鍵詞之上的:try、catch、finally 和 throw。

  • try:一個(gè) try 塊標(biāo)識(shí)了一個(gè)將被激活的特定的異常的代碼塊。后跟一個(gè)或多個(gè) catch 塊。

  • catch:程序通過異常處理程序捕獲異常。catch 關(guān)鍵字表示異常的捕獲。

    無參數(shù) catch 塊 catch 或一般的 catch 塊 catch(Exception ex) 必須是最后一個(gè)塊。 如果在 catch 或 catch(Exception ex) 塊之后還有其他 catch 塊,編譯器將給出錯(cuò)誤。

  • finally:finally 塊用于執(zhí)行給定的語句,不管異常是否被拋出都會(huì)執(zhí)行。例如,如果您打開一個(gè)文件,不管是否出現(xiàn)異常文件都要被關(guān)閉。finally 塊是一個(gè)可選塊,應(yīng)該在 try 或 catch 塊之后。 無論是否發(fā)生異常,finally 塊都將始終執(zhí)行。 finally 塊通常用于清理代碼。注意 不允許添加多個(gè) finally 塊。 此外,finally 塊也不能有 return、continue 或 break 關(guān)鍵字。這個(gè)跟 finally 塊設(shè)計(jì)不符合。

  • throw:當(dāng)問題出現(xiàn)時(shí),程序拋出一個(gè)異常。使用 throw 關(guān)鍵字來完成。

    可以使用 throw 關(guān)鍵字拋出從 Exception 類派生的任何類型的異常。

    throw 使用 new 關(guān)鍵字創(chuàng)建任何有效異常類型的對象。 throw 必須跟從Excpetion派生出來的類型一起使用。

    eg: throw new NullReferenceException("Student object is null.");

try
{
   // 引起異常的語句
}
catch( ExceptionName e1 )
{
   // 錯(cuò)誤處理代碼
}
catch( ExceptionName e2 )
{
   // 錯(cuò)誤處理代碼
}
catch(Exception ex) //捕獲所有
{
   // 錯(cuò)誤處理代碼
}
finally
{
   // 要執(zhí)行的語句
}

LINQ

  1. 什么是LINQ?

答:LINQ(Language-Integrated Query)(集成語言查詢)是一種用于.NET平臺(tái)的查詢和數(shù)據(jù)操作技術(shù)。

它是由微軟開發(fā)的一組語言擴(kuò)展和API,用于在編程語言中執(zhí)行數(shù)據(jù)查詢、轉(zhuǎn)換和操作。LINQ 提供了一種統(tǒng)一的查詢語法,稱為查詢表達(dá)式,讓開發(fā)人員可以使用簡潔的語法來編寫查詢表達(dá)式,而無需將查詢邏輯嵌入到編程語言的代碼中。這種語法類似于SQL語句,但在編譯時(shí)進(jìn)行類型檢查,并集成到編程語言中。

使用查詢表達(dá)式,開發(fā)人員可以以聲明式的方式指定查詢邏輯,而不必顯式編寫循環(huán)和條件語句。

LINQ 的概念基于一組通用的查詢操作,如過濾、排序、分組、投影和聚合等。

它可以用于訪問各種數(shù)據(jù)源,包括對象集合(例如數(shù)組和列表)、數(shù)據(jù)集(例如數(shù)據(jù)庫表)和XML文檔等。

LINQ 還提供了一組強(qiáng)大的標(biāo)準(zhǔn)查詢操作符,如Where、OrderBy、GroupBy、Select、Join、Aggregate等,開發(fā)人員可以使用這些操作符來構(gòu)建復(fù)雜的查詢。此外,LINQ 還支持?jǐn)U展方法語法,允許開發(fā)人員通過鏈?zhǔn)秸{(diào)用方法來編寫查詢。

總之,LINQ 是一種強(qiáng)大的查詢和數(shù)據(jù)操作技術(shù),簡化了開發(fā)人員對各種數(shù)據(jù)源進(jìn)行查詢和操作的過程,提高代碼的可讀性可維護(hù)性。

  1. 關(guān)系圖:

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

LINQ兩種語法形式

LINQ(Language Integrated Query)提供了兩種主要的語法風(fēng)格:查詢語法(Query Syntax)和方法語法(Method Syntax)。這兩種語法風(fēng)格都用于執(zhí)行查詢和操作集合的操作,只是表達(dá)方式上有所不同。

查詢語法

查詢語法是一種更接近自然語言的語法,類似于 SQL 查詢語句。它使用關(guān)鍵字(如 from、where、select)來描述查詢操作。

使用LINQ查詢語法可以方便地對各種數(shù)據(jù)源(如數(shù)組、集合、數(shù)據(jù)庫表等)進(jìn)行查詢、篩選、排序、分組等操作,同時(shí)還能夠進(jìn)行數(shù)據(jù)轉(zhuǎn)換和投影。如下示例:

demo1.cs:

//下面是一個(gè)簡單的LINQ查詢表達(dá)式的示例:
//假設(shè)有一個(gè)包含若干個(gè)整數(shù)的數(shù)組,我們想要篩選出其中所有的偶數(shù):

int[] numbers = { 1, 2, 3, 4, 5, 6 };
IEnumerable<int> evenNumbers = from num in numbers
                               where num % 2 == 0
                               select num;
//在這個(gè)查詢表達(dá)式中,我們使用了關(guān)鍵字“from”、“where”和“select”來描述查詢的過程。
//其中,“from”關(guān)鍵字用于指定數(shù)據(jù)源,這里是整數(shù)數(shù)組“numbers”;
//“where”關(guān)鍵字用于指定篩選條件,這里是“num % 2 == 0”,即判斷數(shù)字是否為偶數(shù);
//“select”關(guān)鍵字用于指定查詢結(jié)果的輸出形式,這里是直接輸出篩選出來的數(shù)字本身。

demo2.cs

//另外,LINQ查詢表達(dá)式還可以進(jìn)行更加復(fù)雜的數(shù)據(jù)處理操作,例如:
var students = new List<Student>
{
    new Student { Name = "Alice", Age = 20, Gender = Gender.Female },
    new Student { Name = "Bob", Age = 22, Gender = Gender.Male },
    new Student { Name = "Charlie", Age = 18, Gender = Gender.Male },
    new Student { Name = "David", Age = 19, Gender = Gender.Male },
    new Student { Name = "Eve", Age = 21, Gender = Gender.Female },
};

var query = from student in students
            where student.Age >= 20
            group student by student.Gender into genderGroups
            select new
            {
                Gender = genderGroups.Key,
                AverageAge = genderGroups.Average(s => s.Age),
                Names = string.Join(", ", genderGroups.Select(s => s.Name)),
            };

foreach (var result in query)
{
    Console.WriteLine($"{result.Gender}: {result.Names} (avg. age = {result.AverageAge})");
}

//在這個(gè)示例中,我們使用了LINQ查詢表達(dá)式對一個(gè)名為“students”的學(xué)生列表進(jìn)行了處理,首先篩選出了年齡大于等于20歲的學(xué)生,然后按照性別進(jìn)行了分組,最終輸出了每個(gè)性別的學(xué)生名單和平均年齡。
//這里使用了“group by”關(guān)鍵字來進(jìn)行分組操作,并對每個(gè)分組進(jìn)行了聚合計(jì)算和投影輸出。
方法語法

方法語法使用方法調(diào)用鏈來實(shí)現(xiàn)查詢。它使用一系列的 LINQ 方法,如 Where()、OrderBy()Select() 等。

示例:

var result = peoples
    .Where(person => person.Age > 25)
    .OrderBy(person => person.Name)
    .Select(person => person);
區(qū)別
  1. 表達(dá)方式:
    • 查詢語法更類似于自然語言,更容易閱讀和理解。
    • 方法語法使用方法調(diào)用鏈,更像是編寫傳統(tǒng)的 C# 代碼。
  2. 可讀性:
    • 查詢語法通常在簡單的查詢場景下更易讀,特別是對于初學(xué)者。
    • 方法語法在復(fù)雜的查詢鏈中可能更具可讀性,特別是對于熟悉編程范式的開發(fā)者。
  3. 適用場景:
    • 查詢語法適用于相對簡單的查詢,尤其是需要強(qiáng)調(diào)查詢邏輯的情況。
    • 方法語法適用于更復(fù)雜的查詢鏈,可以更靈活地進(jìn)行組合和擴(kuò)展。

運(yùn)行時(shí)沒有區(qū)別,編譯后都是一樣的 “查詢語法”看起來更酷,但是“方法語法”更實(shí)用,因此.net開發(fā)者大部分還是用“方法語法”

LINQ常用的擴(kuò)展方法(以方法語法示例)

Linq中提供了大量的類似where的擴(kuò)展方法,簡化數(shù)據(jù)處理,且這些擴(kuò)展方法大部分都在System.Linq命令空間中。

Linq中所有的擴(kuò)展方法幾乎都是針對IEnummerable接口的,能返回集合的都返回IEnumerable,所以是可以把幾乎所有方法“鏈?zhǔn)绞褂谩?/p>

  1. where()方法

    數(shù)據(jù)源中的每一項(xiàng)數(shù)據(jù)都會(huì)經(jīng)過predicate測試,如果針對數(shù)據(jù)源中的每一個(gè)元素,predicate執(zhí)行的返回值為true,那么這個(gè)元素就會(huì)放到返回值中。

    where方法的實(shí)際參數(shù)是一個(gè)lambda表達(dá)式格式的匿名方法,方法的參數(shù)e表示當(dāng)前判斷的元素對象。參數(shù)的名字不一定非要叫e,不過一般lambda表達(dá)式中的變量名長度都不長。

    // example
    int[] nums = new int[]{3,99,88,77,7,8,9,66,15,7};
    IEnumerable<int> items = nums.where(e => e>10);
    
  2. Count()方法: 獲取數(shù)據(jù)條數(shù)

    //example
    int count1 = list.Count(e => e.salary > 1000 || e.Age < 30);
    int count2 = list.where(e => e.salary > 1000 || e.Age < 30).Count();
    
  3. Any()方法:是否至少有一條數(shù)據(jù)

    //example
    bool b1 = list.Any(e => e.salary > 8000);
    bool b2 = list.Where(e => e.salary > 8000).Any();
    
  4. 獲取一條數(shù)據(jù)的相關(guān)API方法

    First()FirstOrDefault():這兩個(gè)方法都用于獲取序列中的第一個(gè)元素

    //First():返回序列的第一個(gè)元素,如果序列為空,會(huì)拋出異常
    var firstElement = list.First();
    
    //FirstOrDefault():返回序列的第一個(gè)元素,如果序列為空,則返回默認(rèn)值(例如,對于 int 類型返回 0)
    var firstOrDefaultElement = list.FirstOrDefault();
    

    Single()SingleOrDefault():這兩個(gè)方法用于獲取序列中的唯一一個(gè)元素。

    //Single():返回序列中的唯一一個(gè)元素。如果序列為空或包含多個(gè)元素,會(huì)拋出異常。
    var singleElement = list.Single();
    
    //SingleOrDefault():返回序列中的唯一一個(gè)元素,如果序列為空,則返回默認(rèn)值;如果序列包含多個(gè)元素,會(huì)拋出異常。
    var singleOrDefaultElement = list.SingleOrDefault();
    
  5. Take()方法: 獲取多條數(shù)據(jù)

    Take 方法用于獲取序列中指定數(shù)量的元素

    //example: Take(5) 表示從序列中取前面的 5 個(gè)元素。
    var takeElements = list.Take(5);
    
  6. 排序的相關(guān)API方法

    OrderBy()OrderByDescending():兩個(gè)方法用于對序列進(jìn)行升序(OrderBy)或降序(OrderByDescending)排列

    //OrderBy(): 按照指定的鍵升序排列
    var orderedList = list.OrderBy(item => item.Property);
    
    //OrderByDescending(): 按照指定的鍵降序排列
    var orderedListDesc = list.OrderByDescending(item => item.Property);
    

    ThenBy()ThenByDescending():這兩個(gè)方法用于在已經(jīng)進(jìn)行排序的基礎(chǔ)上,如果第一個(gè)鍵相同,再按照第二個(gè)鍵再次進(jìn)行升序(ThenBy)或降序(ThenByDescending)排列。

    //ThenBy():在已經(jīng)進(jìn)行排序的基礎(chǔ)上,按照指定的鍵升序排列。
    var thenByList = list.OrderBy(item => item.Property1).ThenBy(item => item.Property2);
    
    //ThenByDescending(): 在已經(jīng)進(jìn)行排序的基礎(chǔ)上,按照指定的鍵降序排列。
    var thenByDescendingList = list.OrderByDescending(item => item.Property1).ThenByDescending(item => item.Property2);
    

    Reverse()方法:用于顛倒序列的順序,即將第一個(gè)元素變成最后一個(gè),第二個(gè)變成倒數(shù)第二個(gè),以此類推。

    var reversedList = list.Reverse();
    
  7. Skip()方法: 限制結(jié)果集,用于跳過序列中的指定數(shù)量的元素,返回剩余的元素。

    //表示跳過序列中的前 5 個(gè)元素,返回剩余的元素。
    var skippedList = list.Skip(5);
    
    //Skip 方法通常與 Take 方法結(jié)合使用,以實(shí)現(xiàn)分頁的效果。
    var page = 2; // 當(dāng)前頁碼
    var pageSize = 10; // 每頁元素?cái)?shù)量
    var resultList = yourList.Skip((page - 1) * pageSize).Take(pageSize);
    
  8. 聚合函數(shù)相關(guān)API方法

    下面的yourList 可以是任何實(shí)現(xiàn) IEnumerable<T> 接口的序列,例如數(shù)組、列表等。

    Sum:

    Sum 方法用于計(jì)算序列中數(shù)值型元素的總和。

    var sum = yourList.Sum();
    

    Average:

    Average 方法用于計(jì)算序列中數(shù)值型元素的平均值。

    var average = yourList.Average();
    

    Min:

    Min 方法用于找到序列中數(shù)值型元素的最小值。

    var min = yourList.Min();
    

    Max:

    Max 方法用于找到序列中數(shù)值型元素的最大值。

    var max = yourList.Max();
    

    Aggregate:

    Aggregate 方法用于通過指定的累加函數(shù)對序列中的元素進(jìn)行累積。

    TResult Aggregate(Func<T, T, T> func)
    

    這個(gè)版本的 Aggregate 接受一個(gè)二元函數(shù)(Func<T, T, T>),該函數(shù)定義了如何累積序列中的元素。函數(shù)接受兩個(gè)參數(shù),表示當(dāng)前的累加值和下一個(gè)元素,返回值表示下一步的累加值。在沒有初始累加值的情況下,Aggregate 將使用序列中的第一個(gè)元素作為初始累加值,然后逐個(gè)遍歷剩余的元素進(jìn)行累積。

    示例:

    var numbers = new List<int> { 1, 2, 3, 4, 5 };
    
    var product = numbers.Aggregate((acc, next) => acc * next);
    
    Console.WriteLine(product); // 輸出: 120
    
    //在這個(gè)例子中,Aggregate 以第一個(gè)元素 1 作為初始累加值,然后通過乘法逐個(gè)累積后續(xù)的元素,得到最終的結(jié)果 120。
    

    Aggregate(帶初始值):

    Aggregate 方法還可以帶一個(gè)初始值,作為累加的起始值。

    TAccumulate Aggregate<TAccumulate>(TAccumulate seed, Func<TAccumulate, T, TAccumulate> func)
    

    這個(gè)版本的 Aggregate 接受一個(gè)初始累加值 seed 和一個(gè)二元函數(shù)(Func<TAccumulate, T, TAccumulate>)。與前一個(gè)版本不同,這個(gè)版本明確指定了初始累加值,然后逐個(gè)遍歷序列中的元素進(jìn)行累積。

    示例:

    var numbers = new List<int> { 1, 2, 3, 4, 5 };
    
    var productWithSeed = numbers.Aggregate(1, (acc, next) => acc * next);
    
    Console.WriteLine(productWithSeed); // 輸出: 120
    
    //在這個(gè)例子中,Aggregate 以初始累加值 1 開始,然后通過乘法逐個(gè)累積后續(xù)的元素,得到最終的結(jié)果 120。
    

    Join:

    Join 方法用于將序列中的元素連接為一個(gè)字符串。

    var joinedString = yourList.Join(", ");
    
  9. GroupBy()方法: 用于按照指定的鍵進(jìn)行分組

    //example
    var students = new List<Student>
    {
        new Student { Name = "Alice", Grade = "A" },
        new Student { Name = "Bob", Grade = "B" },
        new Student { Name = "Charlie", Grade = "A" },
        new Student { Name = "David", Grade = "B" },
        new Student { Name = "Emily", Grade = "A" }
    };
    
    var groupedStudents = students.GroupBy(student => student.Grade);
    
    foreach (var group in groupedStudents)
    {
        Console.WriteLine($"Grade: {group.Key}");
        foreach (var student in group)
        {
            Console.WriteLine($"  {student.Name}");
        }
    }
    
    //輸出結(jié)果如下:
    Grade: A
      Alice
      Charlie
      Emily
    Grade: B
      Bob
      David
    
  10. 投影及相關(guān)API

    在 LINQ(Language Integrated Query)中,投影(Projection)是指從源序列中選擇或變換元素的過程。通過投影,你可以從集合中選擇特定的字段、計(jì)算新的值,或者創(chuàng)建新的對象。在 C# 中,投影通常使用 Select() 方法進(jìn)行,如下示例:

    //基礎(chǔ)示例:
    var fruits = new List<string> { "apple", "orange", "banana", "grape" };
    
    var uppercasedFruits = fruits.Select(fruit => fruit.ToUpper());
    
    foreach (var result in uppercasedFruits)
    {
        Console.WriteLine(result);
    }
    
    //輸出結(jié)果為:
    APPLE
    ORANGE
    BANANA
    GRAPE
    

    LINQ 允許你創(chuàng)建匿名類型來組合源序列中的多個(gè)字段或?qū)傩?,如下示?

    var students = new List<Student>
    {
        new Student { Name = "Alice", Grade = "A" },
        new Student { Name = "Bob", Grade = "B" },
        new Student { Name = "Charlie", Grade = "A" }
    };
    
    var studentDetails = students.Select(student => new {student.Name,student.Grade});
    
    foreach (var detail in studentDetails)
    {
        Console.WriteLine($"Name: {detail.Name}, Grade: {detail.Grade}");
    }
    
    //輸出結(jié)果:
    Name: Alice, Grade: A
    Name: Bob, Grade: B
    Name: Charlie, Grade: A
    

    可以通過 Select() 創(chuàng)建新的自定義類型,將源序列中的元素映射到這個(gè)新類型,如下示例:

    var employees = new List<Employee>
    {
        new Employee { Id = 1, Name = "John", Department = "HR" },
        new Employee { Id = 2, Name = "Alice", Department = "IT" },
        new Employee { Id = 3, Name = "Bob", Department = "Finance" }
    };
    
    var employeeDetails = employees.Select(employee => new EmployeeDetails
    {
        Id = employee.Id,
        FullName = $"{employee.Name} - {employee.Department}"
    });
    
    foreach (var detail in employeeDetails)
    {
        Console.WriteLine($"Employee ID: {detail.Id}, Full Name: {detail.FullName}");
    }
    
    //輸出結(jié)果:
    Employee ID: 1, Full Name: John - HR
    Employee ID: 2, Full Name: Alice - IT
    Employee ID: 3, Full Name: Bob - Finance
    
    1. 集合轉(zhuǎn)換相關(guān)API

      ToArray()方法:用于將 IEnumerable<T> 轉(zhuǎn)換為數(shù)組類型 T[],示例如下:

      var numbers = Enumerable.Range(1, 5);//Enumerable.Range(1, 5) 生成的是一個(gè)整數(shù)序列,它屬于 IEnumerable<int> 類型。在 C# 中,IEnumerable<T> 是表示可枚舉集合的接口,它表示一個(gè)可以按順序逐個(gè)訪問其元素的集合。
      //雖然這個(gè)序列的底層實(shí)現(xiàn)是由 Enumerable.Range 方法創(chuàng)建的,但它并不是一個(gè)數(shù)組(Array)類型。它是一個(gè)通過 IEnumerable<int> 接口提供迭代功能的對象。
      int[] arrayNumbers = numbers.ToArray();
      

      ToList()方法:用于將 IEnumerable<T> 轉(zhuǎn)換為 List<T> 類型,示例如下:

      var fruits = new[] { "apple", "banana", "orange" };
      List<string> fruitList = fruits.ToList();
      
    2. 鏈?zhǔn)秸{(diào)用

      鏈?zhǔn)秸{(diào)用是指在 LINQ 查詢中使用一系列的操作方法,每個(gè)操作方法都返回一個(gè)新的 IEnumerable<T>,從而可以在其上繼續(xù)進(jìn)行操作。常見的鏈?zhǔn)秸{(diào)用包括 Where()、Select()OrderBy()、GroupBy()Skip() 等,如下示例:

      var employees = new List<Employee>
      {
          new Employee { Id = 1, Name = "Alice", Age = 25, Salary = 50000 },
          new Employee { Id = 2, Name = "Bob", Age = 30, Salary = 60000 },
          new Employee { Id = 3, Name = "Charlie", Age = 25, Salary = 55000 },
          new Employee { Id = 4, Name = "David", Age = 35, Salary = 70000 }
      };
      
      var result = employees
          .Where(employee => employee.Id > 2)
          .GroupBy(employee => employee.Age)
          .OrderBy(group => group.Age)
          .Select(group => new
          {
              Age = group.Age,
              Count = group.Count(),
              AverageSalary = group.Average(employee => employee.Salary)
          })
          .Take(3);
      
      //在這個(gè)示例中,鏈?zhǔn)秸{(diào)用首先使用 Where 篩選出 Id > 2 的員工,然后使用 GroupBy 按照年齡分組,接著使用 OrderBy 對分組按照年齡排序,最后使用 Select 投影出年齡、人數(shù)、平均工資的匿名類型,并使用 Take 取前 3 條結(jié)果。
      

LINQ to Object步驟

  1. 引用 LINQ 命名空間:在代碼文件的頂部,使用 using 關(guān)鍵字引用 System.Linq 命名空間,以便使用 LINQ 相關(guān)的類型和擴(kuò)展方法。

  2. 創(chuàng)建數(shù)據(jù)源:定義一個(gè)數(shù)據(jù)源,可以是對象集合、數(shù)組、數(shù)據(jù)集或其他實(shí)現(xiàn)了相應(yīng)接口的數(shù)據(jù)結(jié)構(gòu)。

  3. 構(gòu)建 LINQ 查詢表達(dá)式:使用 LINQ 的查詢語法或方法語法來構(gòu)建查詢表達(dá)式,指定要過濾、排序、投影等的操作。

  4. 執(zhí)行查詢:通過調(diào)用適當(dāng)?shù)牟樵儾僮鞣麃韴?zhí)行查詢。

    查詢操作可以是即時(shí)執(zhí)行的(立即返回結(jié)果),也可以是延遲執(zhí)行的(在需要時(shí)才計(jì)算結(jié)果)。

  5. 處理查詢結(jié)果:使用循環(huán)、條件語句或其他操作來處理查詢結(jié)果,并獲取所需的數(shù)據(jù)。

下面是簡單的 LINQ To Object示例,演示如何對一個(gè)整數(shù)列表進(jìn)行過濾和排序:

using System;
using System.Linq;//引入LINQ

public class Program
{
    public static void Main()
    {
        // 創(chuàng)建數(shù)據(jù)源
        int[] numbers = { 5, 1, 4, 2, 3 };

        // 構(gòu)建 LINQ 查詢表達(dá)式
        var query = from num in numbers
                    where num % 2 == 0 // 過濾偶數(shù)
                    orderby num descending // 按降序排序
                    select num; // 投影選擇的數(shù)字

        // 執(zhí)行查詢并處理結(jié)果
        foreach (var num in query)
        {
            Console.WriteLine(num);
        }
    }
}

//以上示例代碼使用 LINQ 查詢語法,從一個(gè)整數(shù)數(shù)組中過濾出偶數(shù),并按降序排序。然后通過循環(huán)打印出篩選后的結(jié)果。
//通過以上步驟,你可以開始使用 LINQ 來進(jìn)行各種查詢和數(shù)據(jù)操作。

Visual Studio 2019連接MySQL數(shù)據(jù)庫

  1. 要想在 Visual Studio 2019中使用MySQL數(shù)據(jù)庫,首先需要下載MySQL的驅(qū)動(dòng):

    • mysql-connector-odbc-8.0.20-winx64.msi
      鏈接: https://dev.mysql.com/downloads/connector/odbc/.

    • mysql-for-visualstudio-1.2.9.msi
      鏈接:https://dev.mysql.com/downloads/windows/visualstudio/

    • mysql-connector-net-8.0.20.msi
      鏈接:https://dev.mysql.com/downloads/connector/net/8.0.html
      自行下載即可
      下載完后按照以上順序安裝

  2. 安裝完后重啟visual studio

    然后點(diǎn)擊菜單欄的視圖->服務(wù)器資源管理器

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    然后數(shù)據(jù)連接->添加連接

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    你就會(huì)發(fā)現(xiàn)有MySQL 的選項(xiàng)了,進(jìn)入里面配置數(shù)據(jù)庫相關(guān)信息即可。若還是沒有MySQL選項(xiàng),嘗試選擇更高版本的framework

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

Visual Studio 2019 中找不到Linq to SQL 類

在 Visual Studio 2019 中,Linq to SQL 已被標(biāo)記為“過時(shí)”的技術(shù),但仍然可以使用。如果您在 Visual Studio 2019 中找不到 Linq to SQL 類,請按照以下步驟操作:

  1. 確保已安裝 .NET 框架版本 3.5 或更高版本。
  2. 在解決方案資源管理器中,右鍵單擊項(xiàng)目,選擇“添加”>“新建項(xiàng)”。
  3. 在“添加新項(xiàng)”對話框中,選擇“數(shù)據(jù)”類別,然后選擇“LINQ to SQL 類”模板。
  4. 給 Linq to SQL 類命名并點(diǎn)擊“添加”按鈕。
  5. 在 Server Explorer 中連接到數(shù)據(jù)庫。
  6. 在“服務(wù)器資源”窗格中,展開數(shù)據(jù)庫,然后將表拖動(dòng)到 Linq to SQL 類設(shè)計(jì)器中,以創(chuàng)建對應(yīng)的實(shí)體類和 DataContext 類。

如果您仍然無法找到 Linq to SQL 類,請確保已安裝 Linq to SQL 組件。在 Visual Studio 2019 中,您可以通過轉(zhuǎn)到“工具”>“獲取工具和功能”>“修改”>“單個(gè)組件”>“SDK、庫和框架”>“.NET 框架 4.x 開發(fā)人員工具”>“Linq to SQL 工具”來安裝 Linq to SQL 組件?!局苯铀阉飨鄳?yīng)的工具名即可】

LINQ To Sql 步驟

LINQ to SQL 是一種在.NET Framework中使用LINQ查詢關(guān)系數(shù)據(jù)庫的技術(shù)。它通過將數(shù)據(jù)庫架構(gòu)映射到對象模型來簡化數(shù)據(jù)庫的訪問操作。

即: ORM(Object Relation Mapping.

下面是使用 LINQ to SQL 的一般步驟和示例:

  1. 創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu):在關(guān)系數(shù)據(jù)庫中創(chuàng)建并定義操作的表和字段。
  2. 引用 LINQ 命名空間:在代碼文件的頂部,使用 using 關(guān)鍵字引用 System.Linq 命名空間,以便使用 LINQ 相關(guān)的類型和擴(kuò)展方法。
  3. 連接到數(shù)據(jù)庫:添加一個(gè)數(shù)據(jù)連接,連接到目標(biāo)數(shù)據(jù)庫。
  4. 創(chuàng)建對象模型:如果是sql server數(shù)據(jù)庫,可以使用 Visual Studio 工具中的 “LINQ to SQL Classes”,從連接的數(shù)據(jù)庫中提取架構(gòu)信息,自動(dòng)生成與數(shù)據(jù)庫表對應(yīng)的類和屬性。不然就手動(dòng)編寫創(chuàng)建相應(yīng)的數(shù)據(jù)對象模型。
  5. 構(gòu)建查詢:利用生成的對象模型,使用 LINQ 查詢語法或方法語法來構(gòu)建查詢。
  6. 執(zhí)行查詢和操作:通過對查詢結(jié)果執(zhí)行相關(guān)操作,例如排序、更新、插入或刪除數(shù)據(jù)。

下面是一個(gè)簡單的 LINQ to SQL 示例,演示如何查詢數(shù)據(jù)庫中的數(shù)據(jù):

using System;
using System.Linq;//引入

public class Program
{
    public static void Main()
    {
        // 創(chuàng)建 LINQ to SQL 數(shù)據(jù)上下文
        // CRUD都找這個(gè)上下文對象
        using (var db = new MyDataContext())
        {
            // 構(gòu)建查詢(查詢語法格式)
            var query = from c in db.Customers
                        Where c.City == "London"
                        select c;
            
            //構(gòu)建查詢(方法語法格式)
            var query = db.Customers.Where(c => c.City == "London");

            // 執(zhí)行查詢并處理結(jié)果
            foreach (var customer in query)
            {
                Console.WriteLine(customer.CustomerName);
            }
        }
    }
}

//在此示例中,我們首先創(chuàng)建了一個(gè) `MyDataContext` 的 LINQ to SQL 數(shù)據(jù)上下文對象,該對象表示與數(shù)據(jù)庫的連接。然后,我們構(gòu)建了一個(gè)查詢,查詢 `Customers` 表中城市為 “London” 的客戶名。最后,我們通過循環(huán)打印出查詢結(jié)果中的客戶名。

//通過以上步驟,你可以開始使用 LINQ to SQL 來訪問和操作關(guān)系數(shù)據(jù)庫,以更簡潔和直觀的方式進(jìn)行數(shù)據(jù)查詢。

Entity Framework(EF)

如果要操作Mysql數(shù)據(jù)庫,使用第三方庫如Entity Framework,也是可以實(shí)現(xiàn).Net環(huán)境下對MySQL數(shù)據(jù)庫的訪問和操作。

以下是使用Entity Framework配合MySQL進(jìn)行操作的基本步驟:

  1. 打開Visual Studio,創(chuàng)建或打開已有項(xiàng)目。
  2. 打開工具 -> NuGet包管理器 -> 管理解決方案的NuGet程序包。
  3. 在打開的NuGet包管理器中搜索MySql.Data.EntityFramework并安裝。
  4. 在你的模型類上添加[System.Data.Linq.Table(Name = “your_table_name”)]特性,你的列的屬性添加[System.Data.Linq.Column(IsPrimaryKey = true, IsDbGenerated = true)]特性。
  5. 創(chuàng)建你的數(shù)據(jù)上下文類繼承自DbContext,并且創(chuàng)建對應(yīng)的DbSet屬性。

以上就是一種方式來連接MySQL和EF,另外你還可以通過Code First、Database First或者Model First來創(chuàng)建模型。

然而,如果還是想用類似LINQ to SQL的方式來操作MySQL數(shù)據(jù)庫,可以選擇Dapper等其它比較流行的ORM框架。


C# 注意事項(xiàng):

  1. 如下的各個(gè)成對寫法是等效的:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    為什么要有兩種寫法呢?

    因?yàn)槠渲械男戭愋烷_頭的是c#定義的,而大寫類型開頭的則是.Net framework的CLR里的CTS定義的。

    這樣的話不同類型語言到了.Net框架后才可以達(dá)到一個(gè)相同的定義。

    所以小寫屬于c#寫法,大寫屬于.Net框架寫法。

  2. c#中的Struct數(shù)據(jù)類型類似Class類型,但區(qū)別是Class是屬于引用類型,Struct是屬于值類型。

  3. 值類型引用類型在內(nèi)存(棧和堆)中的分配形式:

    c#的值類型是在棧中分配空間,即值存儲(chǔ)在棧中。

    c#的引用類型是在堆中分配空間,棧中只保存其引用。即棧中保存指向堆的地址,堆中保存真實(shí)的對象和數(shù)據(jù)整體。

    如下:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  4. 實(shí)例化一個(gè)對象時(shí),從內(nèi)存角度分析:

    case1:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    case2:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  5. 方法調(diào)用時(shí)形參的值傳遞(默認(rèn)行為,傳遞變量的副本)介紹:

    先說結(jié)論: 值類型的形參引用類型的形參在方法調(diào)用都是值傳遞,跟形參的類型是值類型或者是引用類型都無關(guān)。

    所謂值傳遞: 即copy一份副本出來調(diào)用。

    對于值類型的形參:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    對于引用類型的形參(方法體中不帶有new一個(gè)實(shí)例對象):方法改變的是同一個(gè)對象。

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

    對于引用類型的形參(方法體中帶有new一個(gè)實(shí)例對象):方法改變的是方法中創(chuàng)建的新對象。

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  6. 方法調(diào)用時(shí)形參的引用傳遞:

    由于默認(rèn)值傳遞,要實(shí)現(xiàn)引用傳遞只能做特殊處理。

    • 法一:在形參變量的前面加上ref關(guān)鍵字,同時(shí)傳入實(shí)參時(shí)也在實(shí)參變量前面加上ref關(guān)鍵字。
    • 法二:在形參變量的前面加上out關(guān)鍵字,同時(shí)傳入實(shí)參時(shí)也在實(shí)參變量前面加上out關(guān)鍵字。

    這兩種特殊處理的區(qū)別:

    • ref側(cè)重于改變某一個(gè)值; out側(cè)重于輸出一個(gè)值

    • 法二需要在方法體里面添加一句形參變量的初始化語句才不會(huì)報(bào)錯(cuò),如下:

      #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  7. 對于接收數(shù)字的形參變量的類型定義:

    只要不參與運(yùn)算,方法的形參接收的數(shù)字均定義為string類型。

    只有參與運(yùn)算,形參的數(shù)字才定義為數(shù)字類型。

  8. 小數(shù)會(huì)默認(rèn)被認(rèn)為是double類型,若想標(biāo)識(shí)為float,后面就要加f;同理,若要標(biāo)識(shí)為decimal,后面就要加m,如下所示:

    #c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享

  9. 在類的成員方法中聲明的本地變量(局部變量),在聲明時(shí)還要賦值,因?yàn)椴毁x值其是沒有默認(rèn)值的。如: int age = 5.

    但是.若是定義在類中的成員變量(靜態(tài)變量或?qū)嵗兞?,則不需要賦值,其是有默認(rèn)值的.【Java中也一樣

  10. c#中沒有全局變量的說法,即沒法在類之外定義變量。

    所以全局變量的思路往往采用類的靜態(tài)成員的思路解決。

  11. c#的構(gòu)造函數(shù)注意事項(xiàng):

    • 名字和類名相同。
    • 沒有返回值,void也不行。
    • 使用public作為權(quán)限修飾符,因?yàn)閜rivate的話就不能用new來創(chuàng)建這個(gè)類的對象了。
    • 沒有手動(dòng)寫有參構(gòu)造方法時(shí),一般編譯器會(huì)默認(rèn)生成一個(gè)無參構(gòu)造函數(shù)(并賦值默認(rèn)值),而如果手動(dòng)寫有參構(gòu)造方法,則編譯器不會(huì)默認(rèn)生成一個(gè)無參構(gòu)造函數(shù),需要另外手動(dòng)寫無參構(gòu)造函數(shù)。
  12. c#的完全限定名 = 命名空間 + 類名。

    當(dāng)打印一個(gè)實(shí)例對象時(shí),打印的結(jié)果就是一個(gè)完全限定名。

  13. Object類的ToString()方法:用于將當(dāng)前對象實(shí)例以字符串的形式來表示。

  14. Visual Studio怎么快速多行注釋和取消注釋?

    用鼠標(biāo)選中要注釋的代碼,然后,先按 Ctrl - K 組合鍵,再按 Ctrl - C 組合鍵進(jìn)行注釋。

    如果要取消注釋,就選中被注釋的代碼,然后,先按 Ctrl - K 組合鍵,再按 Ctrl - U 組合鍵即可取消注釋。

  15. C#中的internal關(guān)鍵字限制的訪問范圍是?

    即同一個(gè)項(xiàng)目內(nèi)部是可以訪問的。

    或者同一個(gè)dll文件內(nèi)部是可以訪問的。

    因?yàn)橥ǔ碇v一個(gè)項(xiàng)目生成一個(gè)dll文件

  16. C#中的extern關(guān)鍵字?

    在 C# 中,extern 關(guān)鍵字用于聲明一個(gè)外部方法。它用于指示編譯器該方法的實(shí)現(xiàn)是在外部的,即在當(dāng)前的代碼文件之外,通常是在其他的本機(jī)語言(如 C++)或外部庫中實(shí)現(xiàn)的。使用 extern 關(guān)鍵字聲明的方法不需要提供方法體,因?yàn)樗膶?shí)現(xiàn)在其他地方。這樣可以使 C# 代碼與其他語言或庫進(jìn)行交互。

  17. 在C#中,如果你在數(shù)據(jù)類型后面加上問號(hào)(?),這表示該數(shù)據(jù)類型是可空的。這特別適用于值類型(Value Types),例如整數(shù)(int)、雙精度浮點(diǎn)數(shù)(double)等。通過將其聲明為可空,你可以將其值設(shè)置為null,表示缺少數(shù)值。

    int? nullableInt = null;
    //在這里,nullableInt 是一個(gè)可空整數(shù),可以包含一個(gè)整數(shù)值,也可以是null。這在處理數(shù)據(jù)庫查詢等場景中非常有用,因?yàn)閿?shù)據(jù)庫中的某些字段可能允許為空。
    
  18. 在C#中,?.運(yùn)算符??運(yùn)算符是用于處理可能為null的引用類型的特殊運(yùn)算符:

    ?.運(yùn)算符(Null 條件成員訪問運(yùn)算符):

    ?.運(yùn)算符允許您在訪問引用類型成員之前進(jìn)行空值檢查。

    它的作用是如果左側(cè)的操作數(shù)為null,則整個(gè)表達(dá)式的結(jié)果為null,否則(不為Null)才會(huì)繼續(xù)訪問成員。

    示例:

    int? length = name?.Length;
    Console.WriteLine(length);  // 輸出:null
    //在上面的示例中,如果`name`為null,則`name?.Length`表達(dá)式的結(jié)果將為null,而不會(huì)拋出NullReferenceException。這在避免空引用異常的情況下很有用。
    

    ??運(yùn)算符(null 合并運(yùn)算符): ??運(yùn)算符用于在表達(dá)式中處理可能為null的引用類型,并提供一個(gè)默認(rèn)值,當(dāng)左側(cè)的操作數(shù)為null時(shí)使用該默認(rèn)值。
    示例:

    string displayName = name ?? "Guest";
    Console.WriteLine(displayName);  // 輸出:Guest
    
    //在上面的示例中,如果`name`為null,則`name ?? "Guest"`表達(dá)式的結(jié)果將為"Guest",因?yàn)樽髠?cè)的操作數(shù)為null,所以使用了默認(rèn)值"Guest"。
    

    這些運(yùn)算符也可以結(jié)合使用,如下示例:

    string username = user?.Name ?? "Guest";
    Console.WriteLine(username);
    
    //在上面的示例中,如果`user`為null或者`user.Name`為null,那么`username`將被賦值為"Guest"。否則,它將被賦值為`user.Name`的值。
        
    //這樣可以防止使用null值引發(fā)異常,并提供一個(gè)默認(rèn)值。
    
  19. 所謂二進(jìn)制數(shù)據(jù)的本質(zhì)就是字節(jié)數(shù)組;

    字節(jié)數(shù)組是存儲(chǔ)和傳輸二進(jìn)制數(shù)據(jù)的一種常見方式。

    通過使用字節(jié)數(shù)組,我們可以有效地處理和管理二進(jìn)制數(shù)據(jù),并在計(jì)算機(jī)系統(tǒng)中進(jìn)行傳輸和存儲(chǔ)。

The End!!創(chuàng)作不易,歡迎點(diǎn)贊/評論!!歡迎關(guān)注個(gè)人GZH!!

#c threadclientreceive = new thread(clientreceive); 可以簡化對象初始化,c#,c#,經(jīng)驗(yàn)分享,asp.net,.net,visual studio,知識(shí)分享文章來源地址http://www.zghlxwxcb.cn/news/detail-803219.html

到了這里,關(guān)于【一文詳解】知識(shí)分享:(C#開發(fā)學(xué)習(xí)快速入門)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【Linux系統(tǒng)基礎(chǔ)快速入門詳解】SSH遠(yuǎn)程連接必備網(wǎng)絡(luò)基礎(chǔ)知識(shí)詳解

    【Linux系統(tǒng)基礎(chǔ)快速入門詳解】SSH遠(yuǎn)程連接必備網(wǎng)絡(luò)基礎(chǔ)知識(shí)詳解

    魚弦:CSDN內(nèi)容合伙人、CSDN新星導(dǎo)師、51CTO(Top紅人+專家博主) 、github開源愛好者(go-zero源碼二次開發(fā)、游戲后端架構(gòu) https://github.com/Peakchen) SSH(Secure Shell)是一種網(wǎng)絡(luò)協(xié)議,用于通過加密的方式在不安全的網(wǎng)絡(luò)上安全地進(jìn)行遠(yuǎn)程連接和數(shù)據(jù)傳輸。下面是對SSH遠(yuǎn)程連接的必備

    2024年02月07日
    瀏覽(29)
  • 快速上手MATLAB:科研、工程、數(shù)據(jù)分析,MATLAB入門(下)教你基礎(chǔ)知識(shí)!分享《MATLAB初學(xué)者教程 MATLAB編程-菜鳥入門(清晰版)》

    快速上手MATLAB:科研、工程、數(shù)據(jù)分析,MATLAB入門(下)教你基礎(chǔ)知識(shí)!分享《MATLAB初學(xué)者教程 MATLAB編程-菜鳥入門(清晰版)》

    1、《MATLAB完全學(xué)習(xí)手冊(視頻+課件+代碼)》 2、《MATLAB入門》 3、《詳解MATLAB在科學(xué)計(jì)算中的應(yīng)用》 4、《案例二 MATLAB與Excel交互》 5、《MATLAB初學(xué)者教程 MATLAB編程-菜鳥入門(清晰版)》 6、《MATLAB常用函數(shù)參考 MATLAB函數(shù)匯總 精通MATLAB》 7、等等。。。。 前兩天,我們在(

    2024年02月07日
    瀏覽(110)
  • 開發(fā)基礎(chǔ)知識(shí)-認(rèn)識(shí)Tomcat,Tomcat基礎(chǔ),快速入門Tomcat

    Tomcat 服務(wù)器是一個(gè)免費(fèi)的開放源代碼的Web應(yīng)用服務(wù)器,屬于輕量級(jí)應(yīng)用服務(wù)器,在中小型系統(tǒng)和并發(fā)訪問用戶不是很多的場合下被普遍使用,是開發(fā)和調(diào)試JSP程序的首選。 往細(xì)的方面說:Tomcat 是一個(gè) HTTP 服務(wù)器。HTTP 協(xié)議就是 HTTP 客戶端和 HTTP 服務(wù)器之間的交互數(shù)據(jù)的格式

    2024年02月19日
    瀏覽(261)
  • 知識(shí)分享:一文讀懂AIGC與大模型

    知識(shí)分享:一文讀懂AIGC與大模型

    ? ?什么是大模型? ? ? ? 關(guān)于大模型,有學(xué)者稱之為“大規(guī)模預(yù)訓(xùn)練模型”(large pretrained language model),也有學(xué)者進(jìn)一步提出”基礎(chǔ)模型”(Foundation Models)的概念。 ? ? ?“小模型”:針對特定應(yīng)用場景需求進(jìn)行訓(xùn)練,能完成特定任務(wù),但是換到另外一個(gè)應(yīng)用場景中可能并不

    2024年01月16日
    瀏覽(19)
  • Redis系列學(xué)習(xí)文章分享---第一篇(Redis快速入門之初始Redis--NoSql+安裝redis+客戶端+常用命令)

    Redis系列學(xué)習(xí)文章分享---第一篇(Redis快速入門之初始Redis--NoSql+安裝redis+客戶端+常用命令)

    歡迎小伙伴交流學(xué)習(xí),這是第一篇(Redis快速入門之初始Redis–NoSql+安裝redis+客戶端+常用命令),后續(xù)持續(xù)更新 Redis是一種鍵值型的NoSql數(shù)據(jù)庫,這里有兩個(gè): 鍵值型 NoSql 其中 鍵值型 ,是指Redis中存儲(chǔ)的數(shù)據(jù)都是以key、value對的形式存儲(chǔ),而value的形式多種多樣,可以

    2024年02月20日
    瀏覽(23)
  • 【C++入門】學(xué)習(xí)使用二維數(shù)組基本知識(shí)及用法詳解

    【C++入門】學(xué)習(xí)使用二維數(shù)組基本知識(shí)及用法詳解

    ???♂?iecne個(gè)人主頁: : iecne的學(xué)習(xí)日志 ??每天 關(guān)注 iecne的作品,一起進(jìn)步 ??一起學(xué)習(xí),必看iecne ??希望大家多多支持??一起進(jìn)步呀! 二維數(shù)組就是在一維數(shù)組上多加一個(gè)維度。 建議:以下三種定義方式,利用第二種更加直觀,提高代碼可讀性 第二種就是在定義一

    2024年01月25日
    瀏覽(26)
  • 【學(xué)習(xí)編程】獻(xiàn)給迷茫中的你,教你如何快速入門編程,如何從編程小百到 IT 巨佬?零基礎(chǔ)自學(xué)請收下這份學(xué)習(xí)指南(經(jīng)驗(yàn)分享)

    【學(xué)習(xí)編程】獻(xiàn)給迷茫中的你,教你如何快速入門編程,如何從編程小百到 IT 巨佬?零基礎(chǔ)自學(xué)請收下這份學(xué)習(xí)指南(經(jīng)驗(yàn)分享)

    編程初學(xué)者可能都思考過這個(gè)問題: “我該怎么學(xué)編程?我要選擇哪門編程語言比較適合我呢? ......” 今天小編教大家認(rèn)識(shí)編程語言,了解編程語言。 我們已經(jīng)邁進(jìn)了嶄新的2023年,世界已經(jīng)大范圍的電子化、程序化。 未來世界運(yùn)行規(guī)律大量都是有計(jì)算機(jī)控制,無論大家將

    2023年04月08日
    瀏覽(26)
  • 【零基礎(chǔ)入門學(xué)習(xí)Python---Python中Web開發(fā)基礎(chǔ)之快速入門實(shí)踐】

    【零基礎(chǔ)入門學(xué)習(xí)Python---Python中Web開發(fā)基礎(chǔ)之快速入門實(shí)踐】

    ?? 零基礎(chǔ)入門學(xué)習(xí)Python?? ?? 算法刷題專欄 | 面試必備算法 | 面試高頻算法 ?? ?? 越難的東西,越要努力堅(jiān)持,因?yàn)樗哂泻芨叩膬r(jià)值,算法就是這樣? ?? 作者簡介:碩風(fēng)和煒,CSDN-Java領(lǐng)域優(yōu)質(zhì)創(chuàng)作者??,保研|國家獎(jiǎng)學(xué)金|高中學(xué)習(xí)JAVA|大學(xué)完善JAVA開發(fā)技術(shù)棧|面試刷題

    2024年02月12日
    瀏覽(46)
  • 一文快速入門體驗(yàn) Hibernate

    Hibernate 是一個(gè)優(yōu)秀的持久層的框架,當(dāng)然,雖然現(xiàn)在說用得比較多的是 MyBaits,但是我工作中也不得不接觸 Hibernate,特別是一些老項(xiàng)目需要你維護(hù)的時(shí)候。所以,在此寫下這篇文章,方便自己回顧,也方便新手入門體驗(yàn) Hibernate。 注:使用的版本是 Hibernate 5.x 的 ORM(Object R

    2024年02月03日
    瀏覽(21)
  • 【操作系統(tǒng)】一文快速入門,很適合JAVA后端看

    【操作系統(tǒng)】一文快速入門,很適合JAVA后端看

    作者簡介: 目錄 1.概述 2.CPU管理 3.內(nèi)存管理 4.IO管理 操作系統(tǒng)可以看作一個(gè)計(jì)算機(jī)的管理系統(tǒng),對計(jì)算機(jī)的硬件資源提供了一套完整的管理解決方案。計(jì)算機(jī)的硬件組成有五大模塊:運(yùn)算器、控制器、存儲(chǔ)器、輸入設(shè)備、輸出設(shè)備。操作系統(tǒng)實(shí)際上就是對這個(gè)五塊資源各自提

    2024年02月10日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包