Skip to content

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. 整数类型

类型大小范围后缀默认值
byte1 字节0 ~ 2550
sbyte1 字节-128 ~ 1270
short2 字节-32,768 ~ 32,7670
ushort2 字节0 ~ 65,5350
int4 字节-2,147,483,648 ~ 2,147,483,6470
uint4 字节0 ~ 4,294,967,295u0
long8 字节-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807L0
ulong8 字节0 ~ 18,446,744,073,709,551,615UL0
csharp
int age = 25;
long distance = 9876543210L;
byte level = 255;

2. 浮点类型

类型大小精度范围后缀默认值
float4 字节~7 位小数±1.5e−45 ~ ±3.4e38fF0.0f
double8 字节~15-16 位小数±5.0e−324 ~ ±1.7e308d 或无0.0d
decimal16 字节~28-29 位小数±1.0e−28 ~ ±7.9e28mM0.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 时返回 -1

4. 可空类型的比较

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);      // False

5. 可空类型的常用模式

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);  // 安全

八、类型大小速查

类型大小说明
byte1 字节最小整数
short2 字节短整数
int4 字节默认整数
long8 字节长整数
float4 字节单精度浮点
double8 字节双精度浮点(默认浮点)
decimal16 字节高精度财务计算
bool1 字节布尔值
char2 字节Unicode 字符
DateTime8 字节日期时间

核心知识点总结

类型分类图

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

注意事项

  1. 值类型赋值是复制——修改副本不影响原变量
  2. 引用类型赋值是复制引用——两个变量指向同一对象
  3. 装箱拆箱有性能开销——大量循环中应避免,优先使用泛型集合
  4. struct 适合小数据——过大的 struct 在传递时复制成本高
  5. 枚举提升代码可读性——避免使用魔法数字(magic number)
  6. 可空类型方便处理缺失数据——配合 ?? 运算符使用更简洁
  7. decimal 精确但慢——float/double 快但可能有精度误差
  8. 优先使用泛型集合——List<T> 替代 ArrayList,避免装箱拆箱

Released under the MIT License.