我在这里总结了几种我用过的语法糖,和大家分享下。

语法糖一览:

  • ?? ??= ?. ?[]
  • is as
  • switch 表达式
  • [] ^ ..
  • record with required init

下面我们举个栗子看看:

空值:????=?.

?? 只取值:左边是 null 就用右边,原来的变量不动。

var display = nickName ?? "未命名";
var conn = configuration["Redis"] ?? "localhost:6379";
var title = order.Remark ?? order.Title ?? "无标题";

??= 往回补:左边是 null 才赋值。懒加载最合适。

_counters ??= new Dictionary<string, int>();

对照:var name = input ?? "未命名"input 还是 nullinput ??= "未命名" 是把 input 自己补上。

?? 只认 null,不认空字符串。"" ?? "未命名" 还是 ""

?. / ?[]:中间可能断,安全往下点。整条链任一环是 null,结果就是 null

var city = user?.Address?.City ?? "未知城市";
var first = items?[0];
order?.Refresh();

注意: 逻辑上订单一定有客户,就写 order.Customer.Name,不要?.吞掉异常。

类型判断:isas

以前先 as 再判空,绕。is 一边判断一边取出变量as 只在「转不成就当 null 接着传」时用

if (obj is User user)
    Console.WriteLine(user.Name);

var maybe = payload as User;
SaveOptional(maybe); // 方法接受 User?

不确定类型别强转 (User)obj,不是就抛。新代码优先 is

switch 模式匹配

switch 只能 case 1case "A" 这种常量。现在可以按类型、按属性、按范围匹配,一串 if is 就能收成一个表达式。

按类型:进来的是什么,就当什么用。

string text = payload switch
{
    User u  => u.Name,
    Order o => o.OrderNo,
    string s => s,
    null    => "空",
    _       => "未知"
};

按属性:类型对了还不够,字段也得对上。

string label = order switch
{
    { Status: OrderStatus.Paid, Amount: > 0 } => "可发货",
    { Status: OrderStatus.Pending }           => "待支付",
    { Status: OrderStatus.Cancelled }         => "已取消",
    _                                         => "其它"
};

按范围:运费、会员等级这种分段,别写 if else

var freight = weight switch
{
    < 1  => 8,
    < 5  => 12,
    < 20 => 20,
    _    => 50
};

再加条件:模式对上之后,还要用 when 滤一层。

var discount = order switch
{
    { Amount: >= 500 } when order.IsVip => 0.8m,
    { Amount: >= 500 }                  => 0.9m,
    { Amount: >= 200 }                  => 0.95m,
    _                                   => 1m
};

_ 必须写,否则编译报错。匹配是从上往下,第一条命中就停,所以更具体的写前面。

集合:[]^..

int[] arr = [1, 2, 3];
List<string> names = ["张三", "李四"];
int[] merged = [..arr, 4, 5];

var last = arr[^1];       // 最后一个
var tail = arr[^3..];     // 最后三个
var mid = arr[1..^1];     // 去掉头尾
var page = list[skip..(skip + take)];

手机号后四位:phone[^4..]。左边把类型写清,var x = [1, 2] 有时推不出来。

对象:=>recordwithrequired

身体只有一句,用表达式体:

public string FullName => $"{First} {Last}";
public UserDto ToDto() => new() { Id = Id, Name = Name };

超过两句就写大括号。

DTO、消息、值对象要「按值比较、改一个字段出新副本」,用 record + with

public sealed record OrderDto(long Id, string No, decimal Amount);

var b = a with { Amount = 88 }; // a 不变

EF 里要跟踪改属性的实体,用 class,别 with 出一份新的。

创建时必填用 required,初始化后不许改用 init

public class CreateOrderRequest
{
    public required string ProductId { get; init; }
    public required int Qty { get; init; }
    public string? Remark { get; init; }
}

创建时漏ProductId 编译就报错,可选就不要标required