C#.Net築基-基礎知識

image.png


01、C#基礎概念

1.1、C#簡介

C# (讀作C Sharp)是由微軟公司開發的一種面向對象、類型安全、高效且簡單的編程語言,最初於 2000 年發佈,並隨後成爲 .NET 框架的一部分。所以學習C#語言的同時,也是需要同步學習.NET框架的,不過要要注意C#與.NET的對應版本。

image.png

C#語言和Java類似,是一門簡單易用、應用廣泛的高級編程語言。結合了面向對象編程、事件驅動、泛型編程、異步編程等衆多現代化編程概念,屬於編譯性語言。主要特點:

  • 面向對象:封裝(類與對象)、繼承(類繼承、接口繼承)、多態等(類繼承、多接口繼承實現)。
  • 類型安全:強類型安全,在編譯時檢測,提高代碼可靠性。
  • 交互性,易於各種語言交互,如VB、F#、C++、JavaScript、Python等。
  • GC管理:自動內存管理,C# 採用垃圾回收機制,無需申請、釋放內存,減少內存泄漏風險。
  • 開源跨平臺:.NETCore框架是開源跨平臺的,支持多種操作系統。
  • 強大的標準庫,C#擁有豐富的標準類庫(.NET Framework或.NET Core),內置各種功能和工具。
  • 宇宙第一開發IDE: Visual Studio 提供了強大的開發、調試和設計工具。

image.png

.NET Framework最高支持C#語法版本是C#7.3.NET Standard 2.1,可以基於該版本學習,後面的版本可以根據需要學習新增特性即可。

image.png

圖來源:C#.NET體系圖文概述

1.2、開發環境

📢 推薦安裝Enterprise 企業版!功能最全。開發工具瞭解:《Visual Studio工具使用入門

image.png

1.3、Hello World

using System; //引用using
namespace ConsoleApp_Net48  //申明命名空間
{
    internal class Program  //定義類
    {
        static void Main(string[] args)   //方法,控制檯入口函數
        {
            Console.WriteLine("Hello World!");  //控制檯打印輸出
            Console.ReadLine();
        }
    }
}
  • using 引用命名空間資源。
  • namespace 命名空間 :一組代碼資源(類、結構、枚舉、委託等)的集合。
  • class 類:定義一個類,C#中最常用的代碼組織單元。
  • 方法:特定功能的代碼塊,有輸入和輸出(也可爲空)。

02、基礎語法

C#代碼以行爲單位,(半角)分號;結尾,花括號{ 代碼塊 }爲一個獨立的代碼區域。

image

2.1、變量申明

變量類型 變量名 = 值,變量就是對象值的名字,就像人的名字一樣,通過變量來訪問具體的對象值。變量可以是局部變量、參數、字段、數組、對象實例、委託等。

  • 申明變量、賦值可以一次性,也可分開,也可以一次性申明多個變量。
  • 變量的使用前必須初始化(賦值),使用未賦值的變量會引發異常。
  • 同一作用域內,一個變量名只能申明一次,不可重複。
  • 字符串用“雙引號”,單個字符用'單引號'

也可以用var申明,編譯器通過值類型推斷其具體變量類型,因此申明時必須賦值,var是一個語法糖。

int age; //先申明,後賦值
age = 12;
float weight = 55.55f;
double height = 188.88d; //末尾可以不用帶d,默認就是double
var name = "sam";
var lastName = 'T';
string f1, f2, f3 = "F3"; //申明瞭3個變量,對f3賦值了
var user = new User();    //創建一個User對象實例
User user2 = new User();  //創建一個User對象實例

2.2、代碼風格

C#代碼的命名風格大多爲駝峯命名爲主,相對比較統一,不像前端那麼麻煩,HTML、CSS、JS、URL各不相同。

  • 區分大小寫,字母、數字、下劃線組成,不能數字開頭,不能是關鍵字。C#中的關鍵字還是挺多的,參考 C# 關鍵字
  • 駝峯命名
    • 文件名、類名、接口、方法等都是大駝峯:UserName
    • 局部變量爲小駝峯:userName
    • 字段:下劃線+小駝峯/大駝峯都可以 _userName_UserName,或者"m_"開頭,按照團隊規範即可。
    • 常量:全大寫(下劃線分割),或者大駝峯都可以,USER_NAMEUserName
public string UserName { get => _UserName; set => UserName = value; }

public string _UserName;

public const int Max=100;

public static int MaxAge =100;

private static int _MinAge = 20;

public void Sum(int a, int b)
{
	int sum = a + b;
}

2.3、註釋://

  • 單行註釋//開頭。
  • 多行註釋/*多行註釋 */(同css)
  • XML註釋///用於類型定義、方法、屬性、字段等成員的XML註釋,參考:《C#文檔XML註釋
/// <summary>
/// XML註釋,計算和
/// </summary>
public void Sum(int a, int b)
{
	//單行註釋
	int sum = a + b;
	/*
	多行註釋
	輸出結果
	*/
	Console.WriteLine(sum);
}

2.4、作用域

變量的作用域就是指變量的有效範圍,C#中的作用域可以簡單理解爲 花括號{ 代碼塊 } 的範圍,可以是類、方法、控制邏輯(for、while等),或者就一個單純的{}

  • 一個花括號 {}內代碼爲一個獨立的代碼區域,有獨立的作用域,變量在該作用域內有效。
  • 花括號 {}作用域可以多級嵌套,比如類中包含方法,方法內包括控制邏輯,子作用域可以訪問父級的變量(字段、屬性、方法、具備變量)。簡單理解就是:子級可以訪問父級的成員
private int x = 1; //類字段
void Main()
{ 
	var y = 1 + x; //私有變量
	if (y > 0)
	{
		int z = x + y + 1; //可以訪問父級成員
		Console.WriteLine(z);
		{
			int w = x+y+z+1;  //可以訪問父級成員,及父級的父級
			Console.WriteLine(w);
		}
	}
}

📢一般情況下,變量的作用域是由代碼的詞法環境(就是編寫代碼的位置)來決定的,這比較容易理解。例外情況就是C#中的閉包,常見於動態函數、委託。


03、申明語句

申明變量 說明
Type v 申明指定類型的變量,int xList<int> list
var 隱式匿名類型var,用var申明變量,編譯器根據值推斷出類型變量,因此要求必須賦初始值。
const 申明一個常量,申明時必須賦初始值,且不可修改
ref reference 變量是引用另一個變量(稱爲引用)的變量,可以看做是其別名(分身)
void Main()
{
	int x =100;
	List<int> list = new List<int>();
	List<int> list2 = new();  //前面已知了類型,後面可省略
	int[] arr = [1,2,3];      //C#12的集合表達式,方便的創建數組、集合
    List<int> arr2 = [1,2,3];

    var n = 1;  //匿名類型,自動推斷類型
    var list3 = new List<int>;

    ref int n2 = ref n; //ref另一個變量的別名,n2、n實際指向同一個值,是等效的

    const int max =100; //常量

    var (name,age) = ("sam",18); //多個變量一起申明、賦值,這只是一種簡化的語法糖
    (x, n) = (n, x);             //還可以用該語法交換變量值,非常優雅
}

3.1、const常量

const 常量,顧名思義就是值永遠不會改變的“變量”,可用於局部變量、字段。比如Math.PIInt.MaxValue,用於一些已知的、不會改變的值。

  • 申明常量的同時必須賦初始化值,不可修改,在編譯時值會內聯到代碼中。
  • 常量只能用於C#內置的值類型、枚舉,及字符串。
  • 常量值支持表達式,不過僅限於簡單的運算,要能在編譯時計算出確定的值。
  • 枚舉其實也是常量。
  • 當用定義const 字段時,該常量字段就和靜態字段一樣,屬於類本身,直接使用。
const double r = 5.0;
const double rs = 2 * Pi * r;

📢 要注意常量(包括枚舉)在編譯時是把值內聯到IL代碼中的,因此如果跨程序集引用時,必須一起更新,否則就會出Bug。

image.png

3.2、ref 引用(別名/分身)

ref 關鍵字的核心點就是引用另一個變量的地址,可看做是其別名(分身),指向同一地址。作用和指針操作比較相似,int* y = &x;,不過ref更安全、更方便。
具體在使用上有以下一些場景:

使用場景 說明
引用傳遞參數 方法調用時傳遞引用參數,方法內可修改參數值 ,Foo(ref int number)
ref return 返回一個ref變量,public ref int Foo(ref int n){return ref n;}
ref 變量 引用另一個局部變量,ref int y = ref x
ref 條件表達式 ref 用在三元表達式條件? ref (true):ref (fasle)中,返回引用
ref struct struct完全分配在棧上、不能裝箱,只能用於局部變量、參數,一些高性能的場景
int x = 1;
ref int y = ref x;  //x、y其實同一個變量
Console.WriteLine($"{x},{y}"); //1,1
x++;
Console.WriteLine($"{x},{y}"); //2,2
y++;
Console.WriteLine($"{x},{y}"); //3,3

//換個數組
int[] arr = new int[] { 0, 1, 2};
ref int a = ref arr[0];
a=100;
Console.WriteLine(arr); //100 1 2
  • ref readonly :所指向的變量不能修改值,但可以用ref重新分配一個reference 變量。
  • ref返回值:用於一個方法的返回值,返回一個變量的引用(別名)
void Main()
{
	var arr = new int[] { 1, 2, 3 };
	ref int f = ref GetFirst(arr);
	f = 100;
	Console.WriteLine(arr); //100 2 3
}

private ref int GetFirst(int[] arr)
{
	return  ref arr[0];
}

🔊 在某些場景使用ref可以避免值類型在傳遞時的拷貝操作,從而提高性能,不過不同場景不同,需要具體分析、經過性能測試再確定。


04、常用(控制)語句

語句 說明
if 條件語句,if(true){ 執行 }
if...else 條件語句,if(true){} else(){}
if...else if...else 同上,中間可以接多個else if,不過這個時候一般建議重構下,比如用你switch模式匹配
switch...case 根據條件處理多個分支:switch(條件){ case }。case命中後,注意break結束,否則會繼續執行
while(true){} 循環:條件爲true就會循環執行
dowhile(true) 循環:先執行後判斷條件
for循環 循環:for條件循環,支持多個語句逗號隔開。for(int i =0; i<max; i++)
foreach in 循環元素:foreeach(int item in items),實現了IEnumerable,或有無參數 GetEnumerator()
await foreach foreach的 異步版本
List.ForEach() List<T>自帶的循環執行方法,list.ForEach(s=> s.Dump());
break 跳出循環語句,for、foreach、while、switch、do。跳出最近的語句塊,如果多層嵌套只會對最近的有效
continue 繼續下一次循環,只是後面的代碼不執行了,應用條件同break
return 結束方法/函數並返回結果(若有),注意是針對函數的。
goto 跳轉語句到指定標籤,單獨標籤或者case值,一般不建議使用,goto可讀性不太好
throw 拋出異常,不再執行後面的代碼
try.catch.finally 異常處理,throw 拋出一個異常
checkedunchecked 對整數運算語句進行溢出檢查、不檢查,如果檢查溢出會拋出OverflowException
fixed 申明指針固定一個可移動(回收)變量,防止被GC回收,在unsafe代碼中運行
stackalloc 在堆棧上分配內存,int* ptr = stackalloc int[10]
lock 互斥鎖 Monitor 的語法糖,保障同時只有一個線程訪問共享資源 lock(obj){ }
using 引用命名空間,釋放IDisposable
yield 用於迭代器中返回一個迭代值yield return value,或表示迭代結束yield break

📢 switch 在C#8以上的更多特性,參考後文《C#的模式匹配

4.1、try-catch異常處理

一個標準的異常處理流程:

  • try:功能代碼,需要捕獲異常的地方。
  • catch:捕獲異常,處理異常。支持多個catch語句,捕獲不同的異常,多個catch按照順序執行。catch後面可以用when表達式添加更多篩選條件。
  • finally:最後執行的代碼,無論是否有異常發生都會執行,多用於最後的清理工作。
  • throw:可以拋出一個新的異常,也可以在catch直接throw;,保留原始堆棧信息。

image

	try
	{
		//功能代碼
		throw new ArgumentException("參數name爲null");
	}
	//用when添加更詳細的篩選條件
	catch (ArgumentException e) when (e.InnerException ==null)
	{
		//處理異常,如記錄日誌
	}
	catch (Exception e)
	{
		//處理異常
		throw; //直接throw,保留原始堆棧信息
	}
	finally
	{
		//最後執行的代碼,無論是否有異常發生都會執行,多用於最後的清理工作
	}

📢異步(線程)中的異常一般不會拋出到調用線程(或主線程),只會在await,或獲取Task.Result時纔會被拋出來,更多可查看異步編程相關章節。

4.2、using 的5種用法

using 在C#中有很多中用途,常用來引用命名空間、簡化釋放資源。

using 用途 說明
using namespace 引用命名空間,比較常用,基本每個類都會使用。
global using 項目全局引用,避免每個類都重複using 相同的命名空間。
using 別名 using來創建命名空間或類型的別名,簡化代碼中的使用。
using static 引入一個類型的靜態成員、嵌套類型,代碼中直接使用引入的靜態成員。
using 語句 using 語句可確保正確使用 IDisposable 實例,using(var r){},簡化後無需括號

📢 命名空間 namespace 用於組織代碼(作用域)的主要方式,用關鍵字namespace來命名,可嵌套。C#10 中可以用文件範圍命名空間,減少一層括號嵌套。

  • global using的最佳實現是一般創建一個公共的類文件“Usings.cs”,專門放置項目中全局的公共using
  • using來創建命名空間別名,使用時需要用到操作符::來訪問下級。
  • using可創建任意類型的別名,包括數組、泛型、元祖、指針。
global using System.Text; //全局引用命名空間

using System.Text; //引用命名空間

using json = System.Text.Json.JsonSerializer;  //類型別名
using NumberList = double[];     //類型別名:數組
using Point = (int X, int Y);    //類型別名:元祖ValueTuple<int, int>
using jsons = System.Text.Json;  //空間別名

//namespace myspace; 效果同下,簡化寫法,可節省一對大括號
namespace myspace
{
	public class Program
	{
		void Main()
		{
			json.Serialize(new Object());
			jsons::JsonSerializer.Serialize(new Object()); //這用到操作符::
            NumberList arr = [1,2,3];
		}
	}
}

📢 從.Net6開始,C#項目會根據項目類型隱式包含一些using引用,比如SystemSystem.Text

  • using static,引入一個類型的靜態成員、嵌套類型,代碼中直接使用引入的靜態方法。
using static System.Math;

void  Main()
{
	var a = Abs(-2 * PI ); //直接使用Math下的靜態成員
}

🔸using 語句確保對象在using語句結束時被釋放(調用Dispose)。也可以直接用using申明變量,不用大括號{},這是一種簡化的寫法,會在作用域(方法、語句塊)結束時釋放。

using (StreamReader reader = File.OpenText("numbers.txt"))
{
    Console.WriteLine("do read...");
}
// 簡化寫法,效果和上面一樣,直接用using修飾 變量申明
using StreamReader reader2 = File.OpenText("numbers.txt");

//編譯後的代碼:
StreamReader reader = File.OpenText ("numbers.txt");
try
{
    Console.WriteLine ("do read...");
}
finally
{
    if (reader != null)
    {
        ((IDisposable)reader).Dispose ();
    }
}

📢 using語句是一種語法糖,會自動生成try...finally代碼。


參考資料


©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章