一、實例構造器和類(引用類型)
類(Class) 是一種引用類型(Reference Type),而 實例構造器(Instance Constructor) 是類中用於初始化對象(即類的實例)狀態的特殊方法。它們之間有着非常緊密的聯繫:每當通過 new關鍵字創建一個類的對象時,實例構造器就會被自動調用,以初始化該對象的數據(字段、屬性等)。
- 構造器與類同名
- 沒有返回類型(連 void 也沒有)
- 可以有參數,也可以沒有參數(默認構造器)
- 可以有多個不同參數的構造器(即構造器重載)
- 可以使用
this(...)調用本類其他構造器,避免代碼重複
1.通過實例構造器初始化類對象
public class Person
{
public string Name; // 實例字段
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
Console.WriteLine($"{Name}已創建,年齡{Age}");
}
}
class Program
{
static void Main()
{
// 使用 new 調用實例構造器,創建 Person 類的實例(對象)
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Tim", 18);
}
}
Alice已創建,年齡25 Tim已創建,年齡18
2.提供多種初始化方式(構造器重載)
using Microsoft.Office.Interop.Excel;
public class Car
{
public string Brand; // 實例字段
public int Year;
// 構造器 1:品牌 + 年份
public Car(string brand, int year)
{
Brand = brand;
Year = year;
Console.WriteLine($"{Brand}已創建,年份{Year}");
}
// 構造器 2:只有品牌,年份默認為 2020
public Car(string brand)
{
Brand = brand;
Year = 2020;
Console.WriteLine($"{Brand}已創建,年份2020");
}
}
class Program
{
static void Main()
{
Car c1 = new Car("奔馳", 2023);
Car c2 = new Car("保時捷");
}
}
奔馳已創建,年份2023 保時捷已創建,年份2020
3.使用 this(...)調用其他構造器,避免重複代碼
using Microsoft.Office.Interop.Excel;
public class Car
{
public string Brand; // 實例字段
public int Year;
// 主構造器
public Car(string brand, int year)
{
Brand = brand;
Year = year;
Console.WriteLine($"{Brand}已創建,年份{Year}");
}
// 輔助構造器:只傳品牌,調用主構造器並指定默認年份
public Car(string brand) : this(brand, 2020)
{
}
}
class Program
{
static void Main()
{
Car c1 = new Car("奔馳", 2023);
Car c2 = new Car("保時捷");
}
}
奔馳已創建,年份2023 保時捷已創建,年份2020
using System;
namespace ConsoleApp2
{
internal sealed class Product
{
private int _productId; // 商品 ID(必填)
private string _productName; // 商品名稱(必填)
private decimal _unitPrice; // 單價(必填)
private int _stock; // 庫存(可選,默認 100)
private string _description; // 商品描述(可選,默認空字符串)
// 【構造器 1:只傳必填字段】
public Product(int productId, string productName, decimal unitPrice)
: this(productId, productName, unitPrice, stock: 100, description: "")
{
}
// 【構造器 2:傳必填 + 庫存】
public Product(int productId, string productName, decimal unitPrice, int stock)
: this(productId, productName, unitPrice, stock, description: "")
{
}
// 【構造器 3:傳必填 + 描述】
public Product(int productId, string productName, decimal unitPrice, string description)
: this(productId, productName, unitPrice, stock: 100, description)
{
}
// 【構造器 4:傳所有字段(包括可選)】
public Product(int productId, string productName, decimal unitPrice, int stock, string description)
{
_productId = productId;
_productName = productName;
_unitPrice = unitPrice;
_stock = stock;
_description = description;
}
//打印商品信息
public void PrintProductInfo()
{
Console.WriteLine($"商品ID:{_productId}, 名稱:{_productName}, 單價:{_unitPrice}, 庫存:{_stock}, 描述:{_description}");
}
static void Main()
{
//場景 1:只傳“必填 3 個字段”
Product p1 = new Product(productId: 1001, productName: "無線鼠標", unitPrice: 99.90m);
p1.PrintProductInfo();
//場景 2:傳“必填 + 庫存”
Product p2 = new Product(productId: 1002, productName: "機械鍵盤", unitPrice: 299.00m, stock: 50);
p2.PrintProductInfo();
//場景 3:傳“必填 + 描述”
Product p3 = new Product(productId: 1003, productName: "藍牙耳機", unitPrice: 199.00m, description: "降噪、長續航");
p3.PrintProductInfo();
}
}
}
商品ID:1001, 名稱:無線鼠標, 單價:99.90, 庫存:100, 描述: 商品ID:1002, 名稱:機械鍵盤, 單價:299.00, 庫存:50, 描述: 商品ID:1003, 名稱:藍牙耳機, 單價:199.00, 庫存:100, 描述:降噪、長續航
二、實例構造器和結構(值類型)
- 結構有一個“隱式的無參構造器”
C# 編譯器會為每個結構自動生成一個無參構造器,並且這個構造器不能被手動重寫或刪除。它的作用是:把結構的所有字段設為它們的“默認值”(和類無參構造器的邏輯類似,引用類型字段設 null,值類型字段設 0/false等)。
public struct Point
{
public int X;
public int Y;
// 編譯錯誤!結構不能定義無參實例構造器
// public Point() { }
}
2.使用 new關鍵字 vs 不使用 new
使用 new:當你使用 new調用結構的構造器時,會發生以下事情:
- 在棧上分配內存(如果是局部變量)或從其他存儲位置獲取內存。
- 根據構造器邏輯初始化所有字段。
- 返回一個已初始化的結構體實例。此時,你可以直接使用該實例的成員。
Point p1 = new Point(1, 2); // 分配內存,調用構造器,p1.X 和 p1.Y 已可用
Console.WriteLine(p1.X); // 正常,輸出 1
不使用 new:你也可以不通過 new來創建結構實例。在這種情況下:
- 也會在棧上分配內存。
- 但不會調用任何構造器。
- 所有字段都會被設置為它們的默認值(0, false, null)。
- 該實例處於一種“已分配但未初始化”的狀態。在顯式給所有字段賦值之前,你不能訪問其實例成員(除了
default(Point)的情況)。編譯器會強制檢查這一點。
class Program
{
static void Main()
{
Point p2; // 分配在棧上,但 p2.X 和 p2.Y 暫時不可訪問
//Console.WriteLine(p2.X); // 編譯錯誤!使用了未賦值的局部變量 ‘p2’
p2.X = 10; // 手動賦值
p2.Y = 20;
Console.WriteLine(p2.X); // 輸出 10
}
}
public struct Point
{
public int X;
public int Y;
}
三、類型構造器
類型構造器(靜態構造器) 是一種沒有訪問修飾符(如 public/private)、沒有參數、沒有返回值,並且只能有一個的特殊方法,其作用是對類型級別的靜態字段進行初始化,或者執行類型首次加載時需要運行的邏輯。
|
對比項
|
靜態構造器(類型構造器)
|
實例構造器
|
|
調用對象
|
類型本身(靜態成員)
|
類的實例(對象)
|
|
方法簽名
|
|
|
|
調用時機
|
類型第一次被使用時(自動)
|
通過 |
|
執行次數
|
整個程序運行期間 只執行一次 |
每創建一個對象實例,就執行一次
|
|
參數
|
不能有參數
|
可以有零個或多個參數
|
|
訪問修飾符
|
不能寫(默認私有)
|
可以是 |
|
手動調用
|
不能手動調用
|
可以通過 |
示例 1:基本用法 —— 初始化靜態字段
public class DatabaseConfig
{
// 靜態字段
public static string ConnectionString;
// 靜態構造器
static DatabaseConfig()
{
Console.WriteLine("靜態構造器被調用,初始化數據庫配置...");
ConnectionString = "Server=myServer;Database=myDB;User Id=myUser;";
}
}
class Program
{
static void Main()
{
// 第一次訪問靜態成員,觸發靜態構造器
Console.WriteLine(DatabaseConfig.ConnectionString);
// 再次訪問,不會再次觸發靜態構造器
Console.WriteLine(DatabaseConfig.ConnectionString);
}
}
靜態構造器被調用,初始化數據庫配置... Server=myServer;Database=myDB;User Id=myUser; Server=myServer;Database=myDB;User Id=myUser;
由於靜態構造函數已經在第一行代碼執行前運行過了,並且保證了整個應用程序生命週期內只運行一次,所以第二行代碼不會再執行靜態構造函數體,而是直接去讀取已經初始化好的 ConnectionString字段的值。
示例 2:靜態構造器用於複雜的靜態初始化邏輯
public class Logger
{
public static int LogCount;
static Logger()
{
Console.WriteLine("Logger 類型已加載,初始化日誌系統...");
LogCount = 0;
}
public static void Log(string message)
{
LogCount++;
Console.WriteLine($"[Log #{LogCount}] {message}");
}
}
class Program
{
static void Main()
{
// 第一次調用靜態方法,觸發靜態構造器
Logger.Log("程序啓動");
Logger.Log("加載配置完成");
}
}
Logger 類型已加載,初始化日誌系統... [Log #1] 程序啓動 [Log #2] 加載配置完成
四、操作符重載方法
操作符重載(Operator Overloading為自定義類型(如類或結構體)重新定義或實現內置操作符(如 +, -, ==, !=等)的行為。
示例 1:重載 +操作符(向量相加)
public class Vector2D
{
public double X;
public double Y;
public Vector2D(double x, double y)
{
X = x;
Y = y;
}
// 重載 + 操作符
public static Vector2D operator +(Vector2D v1, Vector2D v2)
{
return new Vector2D(v1.X + v2.X, v1.Y + v2.Y);
}
// 為了方便打印
public override string ToString()
{
return $"({X}, {Y})";
}
}
class Program
{
static void Main()
{
Vector2D v1 = new Vector2D(1, 2);
Vector2D v2 = new Vector2D(3, 4);
Vector2D result = v1 + v2; // 調用我們重載的 + 操作符
Console.WriteLine(result); // 輸出:(4, 6)
}
}