一、實例構造器和類(引用類型)

類(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, 描述:降噪、長續航

二、實例構造器和結構(值類型)

  1. 結構有一個“隱式的無參構造器”

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)、沒有參數、沒有返回值,並且只能有一個的特殊方法,其作用是對類型級別的靜態字段進行初始化,或者執行類型首次加載時需要運行的邏輯

對比項

靜態構造器(類型構造器)

實例構造器

調用對象

類型本身(靜態成員)

類的實例(對象)

方法簽名

static TypeName() { ... }

TypeName() { ... }或帶參數如 TypeName(int x) { ... }

調用時機

類型第一次被使用時(自動)

通過 new TypeName(...)創建對象時(手動或自動)

執行次數

整個程序運行期間 只執行一次

每創建一個對象實例,就執行一次

參數

不能有參數

可以有零個或多個參數

訪問修飾符

不能寫(默認私有)

可以是 publicprivateprotected

手動調用

不能手動調用

可以通過 new手動調用(即創建對象)

示例 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)
    }
}