? 結構體(struct)是值類型(Value Type)
和類(class)不同,結構體在賦值和傳參時是復制值本身,而不是引用地址。
? 一、結構體的基本使用示例:
using System;struct Point
{public int X;public int Y;public Point(int x, int y) // 構造函數{X = x;Y = y;}public void Print(){Console.WriteLine($"X = {X}, Y = {Y}");}
}class Program
{static void Main(){Point p1 = new Point(3, 4);Point p2 = p1; // 值復制p2.X = 100;p1.Print(); // 輸出: X = 3, Y = 4p2.Print(); // 輸出: X = 100, Y = 4}
}
🔍 說明:
p2 = p1
是值復制,修改p2.X
不影響p1.X
。如果是類,就會出現兩個變量共享一份數據的情況。
? 二、結構體和類的區別(簡要對比):
特性 | 結構體(struct) | 類(class) |
---|---|---|
類型 | 值類型 | 引用類型 |
存儲 | 堆棧上 | 堆上 |
繼承 | 不支持繼承 | 支持繼承 |
默認構造函數 | 不可自定義無參構造函數 | 可以定義任意構造函數 |
分配性能 | 更快 | 相對較慢(需要GC) |
? 三、結構體使用場景
結構體適合表示小型的、行為簡單的數據對象,比如:
坐標(Point)
顏色(Color)
時間(DateTime)
金額(Money)
? 四、使用場景擴展:結構體作為方法參數
struct Size
{public int Width;public int Height;public Size(int w, int h){Width = w;Height = h;}
}class Program
{static void ModifySize(Size s){s.Width = 999;}static void Main(){Size mySize = new Size(100, 200);ModifySize(mySize);Console.WriteLine(mySize.Width); // 結果仍是 100,未被修改}
}
🔍 原因:傳值類型是拷貝副本,函數內修改不會影響原始變量。
? 五、ref
修飾結構體參數(傳引用)
static void ModifySize(ref Size s)
{s.Width = 999;
}static void Main()
{Size mySize = new Size(100, 200);ModifySize(ref mySize);Console.WriteLine(mySize.Width); // 現在是 999,修改生效
}
補充:
Q:結構體既然不可自定義無參構造函數,那么它的無參構造函數是真實存在的嗎?
A:
? 結論先行:
在 C# 中,結構體確實有一個無參構造函數,但它是編譯器自動生成的,不允許你自定義或重寫(在 .NET Framework 和 .NET Core/.NET 5 之前如此;.NET 6 起有例外,見后面)。?
? 一、為什么結構體有無參構造函數?
結構體是值類型,值類型在聲明后必須被賦予一個確定的值,所以:
C# 編譯器會默認生成一個“全字段為默認值”的無參構造函數。
你不能自己定義一個無參構造函數(除非是 .NET 6+ 且加
public
)。
? 二、演示:結構體的默認構造行為
struct MyStruct
{public int X;public int Y;
}class Program
{static void Main(){MyStruct s = new MyStruct(); // 編譯通過!Console.WriteLine($"{s.X}, {s.Y}"); // 輸出: 0, 0}
}
即使你沒定義構造函數,new MyStruct()
會把所有字段初始化為默認值(int → 0)。
? 三、不能自定義無參構造函數(.NET 6 前)
下面這個寫法在 .NET 5 或更早版本中會報錯:
struct MyStruct
{public int X;// ? 編譯錯誤:結構體不能包含無參數的構造函數public MyStruct() {X = 1;}
}
錯誤信息(翻譯):
錯誤 CS0568:結構體不能定義顯式的無參數構造函數
真實報錯如圖 VS2022 .NET5
同樣的代碼改成 .NET8 框架就沒事了,允許定義無參構造函數,如圖。
? 四、.NET 6 起的新特性(允許定義無參構造函數)
在 .NET 6 / C# 10 起,你可以寫:
struct MyStruct
{public int X;// ? .NET 6+ 支持!public MyStruct(){X = 42;}
}
但要注意:
new MyStruct()
現在會調用你自定義的構造函數,而不是“全 0 初始化”。只有在啟用了 .NET 6+ 項目環境和編譯器才能生效。
? 五、如果結構體不使用 new
會怎樣?
MyStruct s;
s.X = 10; // ? 合法
Console.WriteLine(s.X); // 10
注意:
不用
new
就要 手動初始化所有字段,否則不能訪問它。
? 總結一下:
特性 | 說明 |
---|---|
是否有無參構造函數 | 有,編譯器默認生成,字段為默認值(int 為 0) |
能否自定義無參構造函數 | ? 在 .NET 6 前不可以;? .NET 6 起可以 |
默認構造函數會干嘛 | 初始化所有字段為類型默認值 |
new struct() vs 不用 new | new :所有字段變為默認值;不用 new:必須手動賦值所有字段 |
?
補充2:
Q:既然結構體不能被繼承,那結構體能繼承類或者接口嗎?
? 結論:
能否繼承 | 答案 |
---|---|
結構體能繼承類嗎 | ? 不能 |
結構體能被繼承嗎 | ? 不能(sealed) |
結構體能繼承接口嗎 | ? 可以 |
? 一、結構體不能繼承類,也不能被繼承
struct MyStruct : MyBaseClass // ? 錯誤:結構體不能繼承類
{
}
會報錯:
錯誤 CS0527: 'MyStruct': 結構體不能從類 'MyBaseClass' 繼承
因為:
結構體是值類型,不支持類的繼承鏈結構
結構體在 CLR(公共語言運行庫)中是 sealed 的
? 二、結構體可以實現接口 ?
interface IPrintable
{void Print();
}struct MyStruct : IPrintable
{public int Value;public void Print(){Console.WriteLine($"Value = {Value}");}
}class Program
{static void Main(){MyStruct s = new MyStruct { Value = 123 };s.Print(); // 輸出:Value = 123// 也可以通過接口調用IPrintable printable = s;printable.Print(); // 仍然輸出:Value = 123}
}
?? 三、結構體實現接口的注意事項(值類型封裝拆箱問題)
結構體是值類型,如果你將它轉換成接口類型,會發生裝箱(Boxing),性能上會有一些開銷:
IPrintable printable = s; // ?? 會進行裝箱,把值類型放到堆上
? 四、總結對比:
特性 | 類(class) | 結構體(struct) |
---|---|---|
是否值類型 | 否(引用類型) | ? 是 |
是否能繼承類 | ? 可以 | ? 不可以 |
是否能實現接口 | ? 可以 | ? 可以 |
是否能被繼承 | ? 可以 | ? 不可以(sealed) |
是否支持虛方法 | ? 可以 | ? 不能虛方法/override(除非顯示接口實現) |
Q:sealed 是什么?
英/si?ld/
adj.密封的;未知的
A:sealed
是 C# 中用于控制 繼承 的關鍵字,主要用于防止類被繼承。
? 一、sealed
是什么?
sealed
修飾符表示這個類(或方法)不能被繼承或重寫。
? 二、語法示例:
🔹1. 阻止類被繼承
sealed class Animal
{public void Speak() => Console.WriteLine("Animal sound");
}// ? 錯誤:不能從密封類繼承
class Dog : Animal { } // 編譯錯誤
📌 報錯信息:
錯誤 CS0509:無法從密封類型 'Animal' 派生
報錯如圖:?
🔹2. 阻止方法被重寫(配合 override
)
你也可以用 sealed
修飾繼承鏈中的方法,阻止再被 override:
class Animal
{public virtual void Speak() => Console.WriteLine("Animal");
}class Dog : Animal
{public sealed override void Speak() => Console.WriteLine("Dog");
}class Husky : Dog
{// ? 錯誤:Speak 已 sealed,不能重寫// public override void Speak() => Console.WriteLine("Husky");
}
?? 三、結構體默認是 sealed 嗎?
是的!
struct MyStruct { }
結構體(struct)默認就是 sealed,不能繼承
所以你 不能在 struct 上顯示寫
sealed
,否則會報錯。
?? 四、sealed 的實際應用場景
應用場景 | 說明 |
---|---|
安全性 | 防止別人繼承你寫的類(尤其是框架庫) |
性能優化 | JIT 編譯器可以優化 sealed 方法的調用路徑(非虛調用) |
明確設計意圖 | 告訴使用者這個類不能被擴展或重寫 |
? 五、sealed 與 abstract 是對立的嗎?
是的,sealed
表示“不能被繼承”,而 abstract
表示“必須被繼承”。
修飾符 | 含義 |
---|---|
sealed | 不能被繼承 |
abstract | 必須被繼承 |
sealed abstract | ? 不能一起用,語義沖突 |
?
? 總結一句話:
sealed
是用于禁止繼承或重寫的關鍵字,常用于類或方法上。結構體本身默認就是 sealed 的,不能被繼承。
?
?
僅供學習參考,如有侵權聯系我刪除。