04-常用数据类型
C# 是强类型语言,所有变量都有明确的数据类型。数据类型决定了数据在内存中的存储方式、大小以及可以执行的操作。
一、值类型 vs 引用类型
这是 C# 类型系统中最重要的概念区分。
核心区别
| 对比项 | 值类型(Value Type) | 引用类型(Reference Type) |
|---|---|---|
| 存储位置 | 栈(Stack) | 栈存引用,堆(Heap)存数据 |
| 变量内容 | 直接包含数据 | 存储对象的地址(引用) |
| 默认值 | 0 或 false 等具体值 | null |
| 赋值行为 | 复制整个数据 | 复制引用(指向同一对象) |
| 作为参数 | 传值(方法内修改不影响原变量) | 传引用(方法内修改影响原对象) |
| 继承 | 不能作为基类(sealed) | 支持继承 |
| 性能 | 轻量,值小(<16 字节)时高效 | 需要额外的 GC 管理 |
内存示意图
栈 (Stack) 堆 (Heap)
─────── ───────
值类型 int a = 10:
┌─────────┐
│ 10 │
└─────────┘
引用类型 string s = "Hello":
┌─────────┐ ┌──────────────┐
│ 0xF0A1 │────→│ "Hello" │
└─────────┘ └──────────────┘
地址/引用 实际数据赋值行为区别
csharp
// 值类型:复制数据
int a = 10;
int b = a; // b 是 a 的副本
b = 20; // 修改 b 不影响 a
Console.WriteLine(a); // 10(独立)
Console.WriteLine(b); // 20
// 引用类型:复制引用
int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1; // arr2 指向同一数组
arr2[0] = 999; // 修改 arr2 影响 arr1
Console.WriteLine(arr1[0]); // 999(被修改了!)二、值类型详解
1. 整数类型
| 类型 | 大小 | 范围 | 后缀 | 默认值 |
|---|---|---|---|---|
byte | 1 字节 | 0 ~ 255 | 无 | 0 |
sbyte | 1 字节 | -128 ~ 127 | 无 | 0 |
short | 2 字节 | -32,768 ~ 32,767 | 无 | 0 |
ushort | 2 字节 | 0 ~ 65,535 | 无 | 0 |
int | 4 字节 | -2,147,483,648 ~ 2,147,483,647 | 无 | 0 |
uint | 4 字节 | 0 ~ 4,294,967,295 | u | 0 |
long | 8 字节 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | L | 0 |
ulong | 8 字节 | 0 ~ 18,446,744,073,709,551,615 | UL | 0 |
csharp
int age = 25;
long distance = 9876543210L;
byte level = 255;2. 浮点类型
| 类型 | 大小 | 精度 | 范围 | 后缀 | 默认值 |
|---|---|---|---|---|---|
float | 4 字节 | ~7 位小数 | ±1.5e−45 ~ ±3.4e38 | f 或 F | 0.0f |
double | 8 字节 | ~15-16 位小数 | ±5.0e−324 ~ ±1.7e308 | d 或无 | 0.0d |
decimal | 16 字节 | ~28-29 位小数 | ±1.0e−28 ~ ±7.9e28 | m 或 M | 0.0m |
csharp
float pi = 3.14f; // float 必须加 f 后缀
double e = 2.71828; // double 是默认浮点类型
decimal price = 99.99m; // decimal 用于财务计算
// float 和 double 有精度问题
float f = 0.1f + 0.2f; // 可能不是精确的 0.3
decimal d = 0.1m + 0.2m; // 精确的 0.3,适合金融计算3. 其他基本值类型
csharp
bool isActive = true; // true / false
char grade = 'A'; // 单个 Unicode 字符,2 字节三、引用类型详解
| 类型 | 说明 | 默认值 |
|---|---|---|
string | 不可变的 Unicode 字符串 | null |
object | 所有类型的基类 | null |
dynamic | 运行时类型检查 | 无默认值 |
class | 自定义引用类型 | null |
array | 相同类型元素的集合 | null |
csharp
string name = "John"; // string——特殊的引用类型
object obj = 42; // object——可存储任意类型
dynamic dyn = "Hello"; // dynamic——运行时确定类型
dyn = 123; // 运行时可改变类型(不推荐滥用)string 的不可变性
csharp
string s = "Hello";
s += " World"; // 实际上创建了新字符串,原字符串不变
// 这在循环中大量拼接时性能极差(应使用 StringBuilder)四、枚举(Enum)
枚举是一组命名常量,用于表示一组有限的可选值。枚举是值类型,底层默认使用 int 存储。
详细内容已移至独立文档:10-枚举
csharp
// 简单示例
public enum Season { Spring, Summer, Autumn, Winter }
Season current = Season.Summer;
Console.WriteLine((int)current); // 1五、结构体(Struct)
结构体是值类型,适合表示轻量级数据对象。struct 不支持继承,但可以实现接口。
详细内容已移至独立文档:20-结构体
csharp
// 简单示例
public struct Point { public int X; public int Y; }六、可空类型(Nullable)
1. 为什么需要可空类型
值类型不能为 null(例如 int 永远是数值),但实际开发中需要表示"值未知"的情况。
2. Nullable 语法
csharp
// 两种等价写法
Nullable<int> nullable1 = null;
int? nullable2 = null; // 推荐语法糖
// 赋值
int? age = null; // 年龄未知
age = 25; // 年龄已知
// 字符串已经是引用类型,可以为 null
string? name = null; // C# 8.0+ 可空引用类型3. 可空类型的成员
csharp
int? number = null;
// HasValue —— 检查是否有值
if (number.HasValue)
Console.WriteLine(number.Value); // 获取值
// Value —— 获取值(如果没有值会抛异常)
// int val = number.Value; // ⚠️ 空时 InvalidOperationException
// GetValueOrDefault —— 安全获取值
int result = number.GetValueOrDefault(); // null 时返回 0(默认值)
int result2 = number.GetValueOrDefault(-1); // null 时返回 -1(指定默认值)
// 简写:空合并运算符
int display = number ?? -1; // null 时返回 -14. 可空类型的比较
csharp
int? a = null;
int? b = 5;
Console.WriteLine(a == null); // True
Console.WriteLine(a == b); // False
Console.WriteLine(a < b); // False(null 不参与比较)
Console.WriteLine(a > b); // False
// 比较规则:两个 null 相等,null 与任何非 null 值不相等
Console.WriteLine(null == null); // True
Console.WriteLine(null == 5); // False5. 可空类型的常用模式
csharp
// 模式 1:数据库字段映射(年龄未知)
int? age = GetAgeFromDatabase();
if (age.HasValue)
Console.WriteLine($"年龄:{age.Value}");
else
Console.WriteLine("年龄未知");
// 模式 2:方法返回值表示可能失败
int? TryParseInt(string s)
{
if (int.TryParse(s, out int result))
return result;
return null; // 解析失败返回 null
}
int? parsed = TryParseInt("123");
if (parsed.HasValue)
Console.WriteLine(parsed.Value);
// 模式 3:可空类型运算
int? x = 10;
int? y = null;
int? z = x + y; // 结果是 null(因为 y 是 null)可空引用类型(C# 8.0+)
csharp
// 默认所有引用类型都是不可为 null 的
string notNull = "Hello";
// string notNull = null; // 编译警告
// 使用 ? 标记可为 null
string? nullable = null; // 明确表示可能为 null
// 使用前需要判空
if (nullable != null)
Console.WriteLine(nullable.Length); // 安全八、类型大小速查
| 类型 | 大小 | 说明 |
|---|---|---|
byte | 1 字节 | 最小整数 |
short | 2 字节 | 短整数 |
int | 4 字节 | 默认整数 |
long | 8 字节 | 长整数 |
float | 4 字节 | 单精度浮点 |
double | 8 字节 | 双精度浮点(默认浮点) |
decimal | 16 字节 | 高精度财务计算 |
bool | 1 字节 | 布尔值 |
char | 2 字节 | Unicode 字符 |
DateTime | 8 字节 | 日期时间 |
核心知识点总结
类型分类图
C# 数据类型
├── 值类型(Value Type)
│ ├── 简单类型:int, long, float, double, char, bool, byte...
│ ├── 枚举(enum)
│ ├── 结构体(struct)
│ └── 可空类型(Nullable<T>)
│
└── 引用类型(Reference Type)
├── string
├── class
├── array
├── interface
└── delegate选择指南
| 场景 | 推荐类型 |
|---|---|
| 整数运算 | int(默认)、long(大数) |
| 浮点运算 | double(默认)、decimal(财务) |
| 文本 | string |
| 单个字符 | char |
| 有限可选值 | enum |
| 轻量数据对象 | struct(< 16 字节) |
| 复杂对象 | class |
| 值可能为空 | int? 等可空类型 |
| 未知类型 | object |
| 真/假 | bool |
注意事项
- 值类型赋值是复制——修改副本不影响原变量
- 引用类型赋值是复制引用——两个变量指向同一对象
- 装箱拆箱有性能开销——大量循环中应避免,优先使用泛型集合
- struct 适合小数据——过大的 struct 在传递时复制成本高
- 枚举提升代码可读性——避免使用魔法数字(magic number)
- 可空类型方便处理缺失数据——配合
??运算符使用更简洁 - decimal 精确但慢——float/double 快但可能有精度误差
- 优先使用泛型集合——
List<T>替代ArrayList,避免装箱拆箱


