✅ 结构体(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 的,不能被继承。
仅供学习参考,如有侵权联系我删除。