不要忽视基础:理解 C# 中的类型
Source: Dev.to
请提供您希望翻译的完整文本(除代码块和 URL 之外的内容),我将把它翻译成简体中文并保持原有的 Markdown 格式。谢谢!
介绍
理解类型是 C# 中最重要的基础之一。每一种类型,无论是什么,最终都关联到一个称为 System.Object 的“大老板”类型。这意味着在幕后,所有类型共享一些共同特性。
示例
// This class automatically inherits from System.Object
class Music { }
// This is the same as above, just written out
class Opera : System.Object { }
无论你创建什么,都是从 System.Object 开始的。这为每个对象提供了诸如 ToString() 和 GetHashCode() 等有用的方法。
C# 中的类型:基础
在 C# 中,类型分为两大类:
- 值类型
- 引用类型
还有一个特殊的组,称为 原始类型,它们是最基本的内置类型。
让我们来逐一拆解!
原始类型
原始类型是 C# 内置的最简单的类型。它们对编译器友好,并直接映射到 .NET 框架中的类型。
示例: int, double, bool, char, byte, short, long, float, decimal
- 原始类型是值类型的一个子集。
- 它们有默认值(
int为0,bool为false,等等)。 - 它们运行快速且高效。
int count = 5; // int is a primitive type
bool isOn = true; // bool is a primitive type
值类型
值类型直接保存它们的实际数据。它们存放在一个叫做 栈 的快速访问区域。当你把一个值类型赋给另一个变量时,会复制该值。
示例: 所有原始类型、struct、enum
- 值类型始终拥有一个值,即使你没有显式设置。
- 它们不能为
null(除非使用可空类型)。 - 赋值会复制值——而不是仅复制引用。
所有值类型(struct、enum、原始类型)都继承自 System.ValueType。
int number = 10; // 值类型,如果未设置默认值为 0
int copy = number; // 复制值 (10)
为什么值类型是 sealed 的?
- 布局不可变性: 它们的内存布局是固定的;如果允许继承,大小和结构将变得不可预测。
- 性能: sealed 的特性让运行时能够优化存储和访问。
- 简洁性: 让值类型可以继承会使赋值、装箱和拆箱变得复杂。
下面的代码无法编译:
struct BasePoint { public int X, Y; }
// 错误!不能从 sealed 类型派生
struct DerivedPoint : BasePoint { public int Z; }
你也不能把值类型作为引用类型的基类:
struct BaseStruct { }
// 错误!不能从值类型派生
class MyClass : BaseStruct { }
如何创建自己的值类型
使用 struct
struct 是用户自定义的值类型。下面是一个简单的二维点:
public struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
Point a = new Point(1, 2);
Point b = a; // 复制值(而不是引用)
b.X = 10;
// a.X 仍然是 1,b.X 是 10
使用 enum
所有枚举都继承自抽象的 System.Enum 类型,而 System.Enum 本身又继承自 System.ValueType。enum 是另一种可以创建的值类型:
public enum DayOfWeek
{
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
struct和enum是创建自定义值类型的唯一方式。- 值类型存放在栈上(或对象内部的内联位置),其行为与引用类型(类)不同。
- 不能从
struct或enum派生,也不能让它们继承除System.ValueType之外的任何类型。
可空值类型
有时你希望值类型能够持有 null(例如,当值尚未设置时)。在 C# 中,你可以使用 可空 类型来实现:
int? score = null; // 'score' can now be null
if (score == null)
{
Console.WriteLine("No score yet!");
}
引用类型
引用类型存储对其数据的引用(地址)。实际的数据位于 堆 上,变量仅指向它。将引用类型赋值给另一个变量时,复制的是引用,而不是实际的数据。
示例: class、string、array、delegate、interface、object
- 引用类型可以为
null(它们可能不指向任何对象)。 - 在引用类型未赋值之前使用会导致
NullReferenceException。 - 赋值操作复制的是引用(地址),而不是数据本身。
string name; // 默认情况下为 null
// 在赋值之前使用 'name' 会导致错误
// Console.WriteLine(name.Length); // NullReferenceException!
内存分配方式:值类型 vs 引用类型
| 方面 | 值类型 | 引用类型 |
|---|---|---|
| 位置 | 栈(或在另一个对象内部内联) | 堆(由垃圾回收器管理) |
| 赋值 | 复制实际数据 | 复制引用(指针) |
| 默认值 | 零初始化(例如 0、false) | null |
| 可空性 | 除非包装(如 int?),否则不可为空 | 可以为 null |
| 继承 | 继承自 System.ValueType(sealed) | 继承自 System.Object |
理解这些差异有助于编写更高效、可预测且无错误的 C# 代码。
Source: …
值类型与引用类型的区别
值类型和引用类型的主要区别在于 它们在内存中的存储方式和位置。
值类型 – 存储在栈上
值类型通常存放在栈中——这是一块快速且简单的内存。当你创建一个值类型变量时,数据就直接保存在该变量本身中。

示例
int number = 5;
int age = 20;
每个变量都直接保存自己的值。
引用类型 – 引用与对象位置
引用类型将 实际的对象数据 存放在 堆 上,但 引用(指向对象的变量)可以存放在多个位置:
| 引用声明的位置 | 引用所在的位置 |
|---|---|
| 方法内部的局部变量 | 栈 |
| 类/结构体的字段 | 所在对象内部(通常在堆上) |
static 字段 | 特殊的静态内存区域 |
无论引用存放在哪里,它始终指向堆上的对象数据。


示例
class Pet { public string Name; }
class Person { public Pet pet; }
void Example()
{
Person p = new Person(); // ‘p’ 是局部变量(引用存放在栈上)
p.pet = new Pet(); // ‘pet’ 是字段(引用存放在堆上的 Person 对象内部)
}
p(指向Person对象的引用)位于栈上,因为它是局部变量。Person对象本身位于堆上。- 在
Person对象内部,pet字段保存的引用指向另一个堆分配的对象(Pet)。
当你进行复制时会发生什么?
值类型复制
int a = 10;
int b = a; // b gets its own copy of the value (10)

引用类型复制
Person p1 = new Person();
Person p2 = p1; // p2 points to the same object as p1

p1 和 p2 都引用内存中的 同一个 对象。通过任一变量修改对象时,另一变量访问时也会看到相同的更改。
摘要
- 原始类型(
int、bool等)是内置的 值类型。 - 值类型 保存实际数据,并位于 栈 上。
- 引用类型 保存一个引用(地址)。该引用可以存放在栈、堆或静态区域,但 对象数据始终位于堆上。
- 为 值类型 赋值会复制数据;为 引用类型 赋值会复制地址。
- C# 中的所有类型最终都派生自
System.Object。
初学者的额外提示
-
A
NullReferenceExceptionmeans you tried to use a reference type that hasn’t been instantiated.
→NullReferenceException表示你尝试使用了尚未实例化的引用类型。 -
Use nullable value types (
int?,double?, …) when you need a value type that can also benull.
→ 当你需要一个也可以为null的值类型时,使用可空值类型(int?、double?、…)。 -
Passing a value type to a method copies its value; passing a reference type copies the reference.
→ 将 值类型 传递给方法会复制其值;将 引用类型 传递给方法会复制引用。
Understanding these basics will help you write safer and more efficient C# code. Happy coding!
理解这些基础知识将帮助你编写更安全、更高效的 C# 代码。祝编码愉快!