C#语法糖(按照实用性排序)

📅 2026/7/7 12:25:56 👁️ 阅读次数 📝 编程学习
C#语法糖(按照实用性排序)

这里整理了C#日常开发最高频、最能提升代码简洁度的语法糖,按实用度排序

语法糖本质是编译器帮你生成等价的完整代码,不影响运行性能,只负责让代码更短、更易读、更少出错。


一、空安全系列(每天都用,优先级最高)

彻底解决层层判空的冗余代码,是C#最实用的语法特性,没有之一。

1. 空条件运算符?./?[]

访问对象的属性/方法/索引前自动判空,对象为null时直接返回null,不会抛空引用异常。

// 原始写法stringicon=null;if(itemInfo!=null&&itemInfo.Data!=null)icon=itemInfo.Data.Icon;// 语法糖写法stringicon=itemInfo?.Data?.Icon;// 数组/索引同理int?firstId=itemList?[0]?.Id;

2. 空合并运算符??

左边值为null时,取右边的默认值,专门处理“拿不到就用默认值”的场景。

// 原始写法stringname=item.Name;if(name==null)name="未知道具";// 语法糖写法stringname=item.Name??"未知道具";// 配合?. 一起用intcount=itemInfo?.Num??0;

3. 空合并赋值??=

左边变量为null时,才把右边的值赋给它,简化延迟初始化。

// 原始写法if(infos==null)infos=newList<ItemInfo>();// 语法糖写法infos??=newList<ItemInfo>();

4. 空包容运算符!

告诉编译器“这个对象我确定不会为null,不用报警告”,Unity开发中非常常用(很多Unity对象生命周期由引擎控制,编译器无法判断)。

// 明确知道GetComponent一定能拿到,消除空警告varcontroller=gameObject.GetComponent<ItemController>()!;

二、类型与声明简化

1. 隐式类型var

编译器自动推导变量类型,代码更简洁,尤其适合长类型名。

// 原始写法Dictionary<int,ItemInfo>itemDict=newDictionary<int,ItemInfo>();// 语法糖写法varitemDict=newDictionary<int,ItemInfo>();

注意:var仍然是强类型,编译后和写全类型完全等价,不是动态类型。

2. 目标类型推导new()(C# 9)

声明变量时,右边new可以省略类型,编译器自动匹配左边的类型,进一步简化。

// 原始写法List<ItemData>list=newList<ItemData>();// 语法糖写法List<ItemData>list=new();

3. 值元组ValueTuple:多返回值神器

方法可以直接返回多个值,不用写out参数、不用自定义结构体/类,游戏逻辑中极其常用。

// 定义方法:直接返回多个结果public(boolisSuccess,intcount,intitemId)CheckItem(inttype){return(true,99,1001);}// 调用:解构接收var(ok,num,id)=CheckItem(1);

4. 解构赋值

可以把对象、元组拆成多个变量一次性赋值。

// 元组解构var(id,name,num)=(1001,"生命药水",10);// 自定义类也可以支持解构(写Deconstruct方法)publicvoidDeconstruct(outintid,outstringname,outintnum){id=Id;name=Name;num=Num;}

三、字符串处理

1. 字符串插值$""

替代string.Format,直接在字符串里嵌变量和表达式,可读性极强。

// 原始写法stringmsg=string.Format("获得道具:{0} x{1}",itemName,count);// 语法糖写法stringmsg=$"获得道具:{itemName}x{count}";// 还可以直接写表达式stringtip=$"剩余数量:{info.Num-cost}";

2. 原始字符串字面量"""..."""(C# 11)

写多行文本、JSON、SQL时不用转义引号和换行,非常干净。

stringjson="""{"id":1001,"name":"生命药水","num":10}""";

四、成员与方法简化

1. 表达式体成员=>

单行的方法、属性、构造函数都可以用=>简写,去掉大括号和return。

// 原始写法publicintTotalNum{get{returnprotos.Sum(o=>o.Num);}}publicstringGetDesc(){returnData.Description;}// 语法糖写法publicintTotalNum=>protos.Sum(o=>o.Num);publicstringGetDesc()=>Data.Description;

2. 扩展方法

给已有的类型“新增”方法,不用修改原类,不用继承,是LINQ的基础,自己写工具方法也极其好用。

// 给int写一个扩展方法publicstaticclassIntExt{publicstaticboolIsPositive(thisintnum)=>num>0;}// 调用:像原生方法一样用if(count.IsPositive()){...}

3. 可选参数 + 命名参数

方法参数给默认值,调用时可以跳过部分参数,还可以按名字传参,不用记参数顺序。

// 定义publicvoidAddItem(intitemId,intnum=1,boolshowTip=true){...}// 调用:只传itemId,其他用默认AddItem(1001);// 命名传参:只改showTip,不用管顺序AddItem(1001,showTip:false);

4. 局部函数

在方法内部定义小函数,只在当前方法内可见,比委托更干净,性能更好。

publicvoidProcessItems(List<Item>items){// 内部辅助函数boolIsValid(Itemitem)=>item.Num>0&&item.ExpTime==0;foreach(variteminitems){if(IsValid(item)){...}}}

五、LINQ 集合操作(集合处理神器)

LINQ是C#处理集合的大杀器,几乎所有集合遍历、筛选、转换都可以用它一行写完,替代大量foreach循环。

以下是最高频的10个方法:

List<ItemInfo>infos=...;// 1. 筛选:WherevarvalidItems=infos.Where(x=>x.Num>0).ToList();// 2. 转换/映射:SelectvarallItemIds=infos.Select(x=>x.Data.Id).ToList();// 3. 取第一个/第一个默认:First / FirstOrDefaultvarfirstItem=infos.First(x=>x.Data.Id==1001);varitem=infos.FirstOrDefault(x=>x.Data.Id==1001);// 4. 是否存在:AnyboolhasItem=infos.Any(x=>x.Data.Id==1001);// 5. 全部满足:AllboolallEnough=infos.All(x=>x.Num>=5);// 6. 计数/求和:Count / SuminttotalCount=infos.Sum(x=>x.Num);intrareCount=infos.Count(x=>x.Data.Grade>=3);// 7. 排序:OrderBy / OrderByDescendingvarsorted=infos.OrderByDescending(x=>x.Data.Grade).ToList();// 8. 转列表/数组:ToList() / ToArray()// (把IEnumerable转为实际集合,最常用)// 9. 分组:GroupByvargroups=infos.GroupBy(x=>x.Data.Type);// 10. 字典转换:ToDictionaryvardict=infos.ToDictionary(x=>x.Data.Id,x=>x);

六、模式匹配(替代冗余if-else)

1.is类型模式 + 变量声明

判断类型的同时直接声明变量,不用二次强转。

// 原始写法if(protoisLists){Listslist=(Lists)proto;...}// 语法糖写法if(protoisListslist){// 直接用list变量items=list.Items.ToList();}

2.switch表达式(C# 8)

把switch语句写成表达式,更简洁,适合多分支返回值的场景。

// 原始写法stringGetQualityName(intgrade){switch(grade){case1:return"普通";case2:return"稀有";case3:return"史诗";default:return"未知";}}// 语法糖写法stringGetQualityName(intgrade)=>gradeswitch{1=>"普通",2=>"稀有",3=>"史诗",_=>"未知"};

3. 属性模式

直接匹配对象的属性值,写复杂分支判断非常干净。

stringGetItemDesc(ItemInfoinfo)=>infoswitch{{Num:0}=>"未拥有",{Data.Grade:>=3,Num:>10}=>"大量稀有道具",{Data.Type:2}=>"材料类道具",_=>"普通道具"};

七、其他高频实用

1.nameof运算符

获取变量、类、方法、属性的名字字符串,避免硬编码,改名字时编译器自动同步。

// 原始写法Debug.LogError("ItemController 初始化失败");// 语法糖写法:改类名时自动跟着变Debug.LogError($"{nameof(ItemController)}初始化失败");

2. 三元运算符?:

最基础也最常用的单行条件赋值。

// 原始写法stringtext;if(num>0)text="充足";elsetext="不足";// 语法糖写法stringtext=num>0?"充足":"不足";

3. 索引与范围^/..(C# 8)

快速取数组/集合的末尾元素和片段。

int[]arr={1,2,3,4,5};intlast=arr[^1];// 最后一个元素 = 5intsecondLast=arr[^2];// 倒数第二个 = 4int[]part=arr[1..3];// 索引1到2的元素 = {2,3}

4.using声明(C# 8)

自动释放IDisposable对象,不用写大括号,作用域到当前方法结束自动释放。

// 原始写法using(varstream=newFileStream(path,FileMode.Open)){...}// 语法糖写法usingvarstream=newFileStream(path,FileMode.Open);// 方法结束自动释放

5.record记录类型(C# 9)

专门用来做不可变数据载体,自带值比较、ToString、拷贝,非常适合传配置、传数据快照。

// 定义一行搞定,自动生成构造函数、属性、Equals、ToStringpublicrecordItemSnapshot(intId,stringName,intNum);// 使用varsnap=newItemSnapshot(1001,"药水",10);varnewSnap=snapwith{Num=20};// 拷贝并修改部分字段

学习优先级建议

  1. 第一梯队(必掌握):空安全系列、字符串插值、var、LINQ基础、表达式体成员
  2. 第二梯队(大幅提效):值元组、扩展方法、is模式匹配、switch表达式、nameof
  3. 第三梯队(按需使用):record、索引范围、原始字符串、属性模式

这些特性在Unity 2021及以上版本都已完整支持,放心用在项目里即可。