1. 入门
想象一下这种情况:你在代码里存猫的数据。除了猫的年龄,你还想存点类似“没数据”、“年龄未知”这种状态。你可以存0,但0其实是个合法的年龄。
int age = 0; // 猫的年龄。但0到底是小猫还是“未知”?
问题在于,int只能存整数,没法表达“没数据”。如果年龄是字符串,那你可以赋null。但数字类型就不行:
int age = null; // 错误! 不能给int类型变量赋null
这是因为值类型(像struct,比如int、double、DateTime、bool)总得有个值。它们没有“空”的状态(跟引用类型不一样,引用类型的null就是没有对象)。
生活中的例子:
有时候程序员会想出“特殊值”来绕过这个限制,比如-1或者int.MaxValue表示“没值”。但这样又丑又麻烦,还容易把真实值和占位符搞混。
2. Nullable类型:可以为null的类型
思路
要是普通数字(其实所有值类型)也能存null就好了吧?
C#里这事儿靠一个特殊的包装类Nullable<T>搞定。这个类可以存一个T类型的值,也可以啥都不存(就是null)。
语法
int? age = null; // age变量可以是数字,也可以是null
C#编译器看到这种写法,其实等价于:
Nullable<int> age = new Nullable<int>(); // age变量没有值
或者你想直接赋值:
Nullable<int> age = new Nullable<int>(42); // age存了42
所以,问号能把任何值类型变成它的nullable版本:
- int?(其实就是Nullable<int>)
- double?
- DateTime?
- 还有各种struct都行
常用struct的Nullable包装
| 普通类型 | Nullable类型 | 写法示例 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
你也可以写得更长,用包装类的写法:
Nullable<int> pages = null;
但写int?更短更酷。
3. 怎么用nullable类型
赋值和判断
赋null跟引用类型一样简单:
声明和赋值
int? temperature = null;
temperature = 25;
判断是不是null
怎么知道nullable类型里有没有值?
if (temperature != null)
{
Console.WriteLine($"温度: {temperature}");
}
else
{
Console.WriteLine("没有温度数据");
}
Nullable类型的属性:.HasValue 和 .Value
nullable类型有两个重要属性:
- .HasValue —— 如果变量有值(不是null)就返回true。
- .Value —— 变量的值(如果有,否则会抛异常)。
int? temperature = null;
if (temperature.HasValue) //不会抛异常!
{
Console.WriteLine($"温度: {temperature.Value}°C");
}
else
{
Console.WriteLine("温度未知");
}
这段代码没问题:temperature不是null,它里面存的是null。你随时可以用Nullable类型的HasValue属性。但如果你在没值的时候取.Value,就会抛InvalidOperationException。所以一定要先判断!
更简洁的输出写法
很多时候直接用nullable类型变量就行,C#自己会处理:
int? hours = null;
Console.WriteLine(hours); // 什么都不输出(就是空)
hours = 10;
Console.WriteLine(hours); // 输出10
4. 运算和nullable类型:算术、比较、类型转换
运算:会发生什么?
只要有一个操作数是nullable,结果也是nullable。
如果有一个操作数是null,结果也会是null。
int? a = 5;
int? b = null;
int? sum = a + b; // sum == null
int? c = 10;
int? d = 15;
int? total = c + d; // total == 25
比较运算
nullable类型可以和普通数字比较:
int? score = null;
if (score > 0) Console.WriteLine("很棒的结果!");
else Console.WriteLine("没有结果"); // 会走这里
如果score是null,score > 0会是false。
显式和隐式类型转换
- 普通类型转nullable:自动搞定。
- nullable转普通类型:只有不是null才行(否则抛异常)。
int? x = 3;
int y = x.Value; // 只要x不是null就OK
// 隐式转换
int? z = y;
5. Nullable和方法:参数和返回值
返回nullable值
如果方法不一定能返回结果,可以用nullable类型做返回值:
// 方法根据登录名找用户,找到就返回年龄,否则null
int? FindUserAge(string login)
{
// ... 这里是查找逻辑
return null; // 没找到
}
Nullable参数
参数也可以用nullable类型,表示“可能有,也可能没有”。
void PrintTemperature(int? temp)
{
if (temp == null)
Console.WriteLine("温度未知");
else
Console.WriteLine($"温度: {temp}度");
}
GO TO FULL VERSION