计算机语言本身也作为一项技术在发展,而一些新的语言语法特性或者是语法糖并不被开发人员所熟知,有效地使用这些语言特性可以大大提高编码效率。
另外,社区中很多人说微软放弃了VB,这个说法有那么点意思,但是不够准确,我认为微软是在把VB的语言优势融合到C#。
这里我盘点几个实用的C#语言语法特性 ,这些语法特性并不一定是最新C#12的,但是是我最近看别人代码时注意到的问题:
对象初始化语法
这个语法印象中是 C#3还是4,VB10引入的,不算新,这里提出来是为了下一个的小节的引入。
VB.NET 中的 With 关键字是用于在创建对象实例时初始化对象的属性值,这个语法称为对象初始化器,这样设计的目的是为了使代码结构性更强、表意更清晰:
Class Person
Public Property Name As String
Public Property Age As Integer
End Class
Module Program
Sub Main(args As String())
'VB.NET中的对象初始化语法
Dim obj = New Person With {.Name = "a", .Age = 1}
End Sub
End Module
而在 C# 中则不需要 With 关键字。
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
//C# 中的对象初始化语法
Person? person = new Person { Name = "a", Age = 1 };
with 表达式
C#中的 with 和 VB.NET中的 With 用法不同。
在C#中称为with表达式,它基于with关键字,用于生成创建一个对象实例的副本,并修改其某个或多个属性或字段值。
with 表达式是基于对象初始化器语法的扩展。
using System;
using System.Collections.Generic;
public class ExampleWithReferenceType
{
public record TaggedNumber(int Number, List<string> Tags)
{
public string PrintTags() => string.Join(", ", Tags);
}
public static void Main()
{
var original = new TaggedNumber(1, new List<string> { "A", "B" });
var copy = original with { Number = 2 };
Console.WriteLine($"Tags of {nameof(copy)}: {copy.PrintTags()}");
// output: Tags of copy: A, B
original.Tags.Add("C");
Console.WriteLine($"Tags of {nameof(copy)}: {copy.PrintTags()}");
// output: Tags of copy: A, B, C
}
}
with 表达式作责操作数可以是一个 record 类型(https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/record)。从C# 10开始,with 表达式左侧操作数也可以为 struct 类型(https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/struct) 或 匿名类型(https://learn.microsoft.com/zh-cn/dotnet/csharp/fundamentals/types/anonymous-types)。
详情参阅:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/with-expression
is 关键字和 null 判断
微软正在放弃 instance == null 这样的检查 ,因为这样的语法歧义太大,可读性不强。
那么还是学习了VB.NET 使用可读性更好的 instance is null 这样的语法。
同时即使是引用类型,可空的话还是建议加 ? ,如:
SomeType? st = null
空引用初始化语法
我定义一个只能通过其 Instance 静态属性访问其实例成员的类 ,这里我使用了懒惰初始化语法:
public class SomeClass
{
private SomeClass() { }
private static SomeClass s_static_insatance;
public SomeClass Instance
{
get
{
if (s_static_insatance == null)
//或 if (s_static_insatance is null)
s_static_insatance = new SomeClass();
return s_static_insatance;
}
}
}
现在我可以这样写:
public class SomeClass
{
private SomeClass() { }
private static SomeClass s_static_insatance;
public SomeClass Instance
{
get
{
return s_static_insatance ??= new SomeClass();
}
}
}
当然,也可以简化成这样:
public class SomeClass
{
private SomeClass() { }
private static SomeClass s_static_insatance;
public SomeClass Instance => s_static_insatance ??= new SomeClass();
}
弃元
就是“放弃"、"丢弃”的意思 , 符号为 _
应用于由返回值的方法时,你虽然可以直接调用方法而直接忽略返回值,但这样的表意不够清晰,于是最好这样 _ = SomeFunction(); 来强化表意。
其实弃元更主要的场景是与值元组结合使用,当你想忽略值元组中的某个元素时则可使用:
(_, _, area) = city.GetCityInformation(cityName);
利用空引用初始化语法来做判断
这里结合了弃元的使用。
本来我要这样写:
public static void DoSome(object obj)
{
if (obj == null) //或 if (obj is null)
throw new ArgumentNullException(nameof(obj));
}
现在我可以这样写:
public static void DoSome2(object obj)
{
_ = obj ?? throw new ArgumentNullException(nameof(obj));
}
二进制字面量
直接写数字(数值字面量)默认为10进制。
int a = 123;
float b = 3.14f;
double c = 3.14;
加 "0x" 前缀可以变成16进制数字字面量。
int a = 0xff;
现在可以加 "0b" 成为2进制字面量。
int a = 0b1010;
还可以按位分割:
a = 1_2_3;
a = 0x_1_2_3;
a = 0b_10_1_0;