C# 中变量定义带 ? 及 ?? 的含义及用法 1
正文
?
C# 语法中一个问号(?)的运算符是指:可以为 null 的类型。
MSDN 上面的解释:
在处理数据库和其他包含不可赋值的元素的数据类型时,将
null赋值给 数值类型 或 布尔型 以及 日期类型 的功能特别有用。
例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义。
??
C# 语法中两个问号(??)的运算符是指 null 合并运算符,合并运算符为类型转换定义了一个预设值,以防可空类型的值为 null。
MSDN 上面的解释:
??运算符称为null合并运算符,用于定义可以为null值的类型和引用类型的 默认值。如果此运算符的左操作数不为null,则此运算符将返回左操作数(左边表达式);否则当左操作数为null,返回右操作数(右边表达式)。
int? x = null;//定义可空类型变量
int? y = x ?? 1000;//使用合并运算符,当变量 x 为 null 时,预设赋值 1000
Console.WriteLine(y.ToString()); //1000
/// <summary>
/// Gets a single instance
/// </summary>
public static Log LogInstance
{
get
{
return _log ?? (_log = new Log()); //如果此运算符的左操作数不为 null,则此运算符将返回左操作数;否则返回右操作数。
}
}
C# 中 ?、 ??、 ?: 、?.、?[ ] 2
1. 可空类型修饰符 ?
引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空。
例如:
string str=null;是正确的,int i=null;编译器就会报错。
为了使值类型也可为空,就可以使用可空类型,即用可空类型修饰符 ? 来表示,表现形式为 T?
例如:
int?表示可空的整型,
DateTime?表示可为空的时间。
T? 其实是 System.Nullable(泛型结构)的缩写形式,
也就意味着当你用到 T? 时编译器编译时会把 T? 编译成 System.Nullable 的形式。
例如:
int?,编译后便是System.Nullable的形式。
2. 三元(运算符)表达式 ?:
例如:
x?y:z表示如果表达式x为true,则返回y;如果
x为false,则返回z,是省略if{}else{}的简单形式。
3. 空合并运算符 ??
用于定义可空类型和引用类型的 默认值。
如果此运算符的左操作数不为 null,则此运算符将返回左操作数,否则返回右操作数。
例如:
a??b当a为null时则返回b,a不为null时则返回a本身。
空合并运算符为 右 结合运算符,即操作时 从右向左 进行组合的。
如,
a??b??c的形式按a??(b??c)计算。
4. NULL 检查运算符 ?.
例如我们要获取一个 Point 序列的第一个点的 X 坐标,第一感觉会这么写:
int firstX = points.First().X;
但是,老鸟会告诉你,这儿没有进行 NULL 检查,正确的版本是这样的:
int? firstX = null;
if (points != null)
{
var first = points.FirstOrDefault();
if (first != null)
firstX = first.X;
}
正确倒是正确了,代码取变得难读多了。在 C# 6.0 中,引入了一个 ?. 的运算符,前面的代码可以改成如下形式:
int? firstX = points?.FirstOrDefault()?.X;
从这个例子中我们也可以看出它的基本用法:如果对象为 NULL,则不进行后面的获取成员的运算,直接返回NULL
需要 注意 的是,由于 ?. 运算符返回的可以是 NULL,当返回的成员类型是 struct 类型的时候,?. 和 . 运算符的返回值类型是不一样的。
Point p = new Point(3, 2);
Console.WriteLine(p.X.GetType() == typeof(int)); //true
Console.WriteLine(p?.X.GetType() == typeof(int?)); //true
5 ?[] 运算符:
int? first = customers?[0].Orders.Count();
来自豆包的解释
这段代码是 C# 中用于检查一个名为
customers的集合的第一个元素是否存在。
首先,
customers?[0]表示检查customers集合是否为空(null)。?操作符是一个空值合并操作符,它允许在访问集合中的元素之前检查集合是否存在。这里,customers?[0]表示尝试访问customers集合的第一个元素。==如果customers存在且它的第一个元素存在,代码将继续执行==;如果customers为null或者它没有任何元素,整个表达式将返回null。然后,
Orders.Count()表示获取customers集合的第一个元素的Orders集合的计数。Orders可能是customers集合中每个元素的一个属性,它指向一个Order对象的集合。Count()方法返回这个Order对象集合的数量。综上所述,
customers?[0].Orders.Count()这个表达式的意思是:尝试获取customers集合的第一个元素,并返回这个元素的Orders集合的数量。如果customers或者它的第一个元素不存在,整个表达式将返回null。请注意,
int?类型表示一个可空整数,即它可以存储null值。这使得表达式的结果可以是一个整数或者null,具体取决于customers集合的状态。这种设计提供了一种灵活的方式来处理可能为空的集合,避免了运行时错误的发生。
C# 中 "?"(问号)相关语法糖 3
c# 中与 ?(问号有关的语法糖大概有这几个:??、 ?、 ?.、?[] 、?: ,在前面已经有详细说明。总结来看,除了 ?: 这个三元(运算符)表达式外,都是和是否空有关。但文章中对 ?[] 未做更多描述,所以在这里做个补充。看看下面的栗子:
string[] arr = null;
Console.WriteLine(arr[0]);
上面代码将报错:“Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.”
string[] arr = null;
Console.WriteLine(arr?[0]);
那么上面代码 不会报错,而是输出空白。
string[] arr = Array.Empty<string>();
Console.WriteLine(arr?[0]);
那么上面代码依旧会报错:“System.IndexOutOfRangeException:“Index was outside the bounds of the array.””。也就是 ? 只能判断某个值是否为 null,而无法判断 是否是空数组。
int?[] arr = new int?[5];
arr[1] = 99;
arr[3] = 321;
foreach (int? i in arr)
{
Console.WriteLine(i);
}
输出是什么?
99
321
如果把 ? 去掉,其余不变,也就是这样:
int[] arr = new int[5];
那么输出是什么?
0
99
0
321
0
所以,本质上 ? 都是判断是否为 null。对于上面的例子,如果不加 ?,那么对于 int [] 这样的数组,即便不做初始化,系统也会自动给默认值,而这很容易造成错误 —— 因为你不知道数组中这个值到底是系统给的,还是你自己设置的。而当加了 ?,允许为空后,那么就不一样了,系统是不会给出默认值的,从而避免了一些不必要的错误 —— 这种错误往往还很难发现。