C# 9.0 特性全面总结
顶级语句
顶级语句可以删除程序中不必要的代码,以最简单的Hello,world!为例:
usingSystem;
namespaceHelloWorld{
classProgram{
staticvoidMain(string[]args){
Console.WriteLine("HelloWorld!");
}
}
}
如果使用顶级语句的话,可以简化为:
usingSystem;
Console.WriteLine("HelloWorld!");
如果不使用using,还可以更加简化:
System.Console.WriteLine("HelloWorld!");
顶级语句在很多命令行程序、小工具程序中会非常有用,对应用程序的作用域或者复杂程度没有任何限制。
注意,一个程序中,只能有一个文件使用顶级语句,并且顶级语句必须位于命名空间或类型定义之前!
弃元参数
在lambda表达式或者匿名函数中如果要忽略某个参数,可以用_代替。
varbutton=newButton("ClickMe!");
button.Click+=(_,e)=>{/*othercodegoeshere.*/};
仅初始化设置器(Initonlysetters)
创建只能通过对象初始化进行赋值的属性。
publicclassInitDemo{
publicstringStart{get;init;}
publicstringStop{get;init;}
}
//initDemo.Start="Now";//Error
//initDemo.End="Tomorrow";//Error
varinitDemo=newInitDemo{
Start="Now",
Stop="Tomorrow"
};
记录类型(Record)
记录类型,是一种引用类型,默认是不可变的。记录类型的相等判断可以通过引用或者结构进行判断的。
//默认不可变的记录类型
publicrecordPerson(stringName,intAge);
//可变记录类型
publicrecordMutablePerson(stringName,intAge){
publicstringName{get;set;}=Name;
publicintAge{get;set;}=Age;
}
varperson1=newPerson("ZhiminZhang",40);
varperson2=newPerson("ZhiminZhang",40);
Console.WriteLine(person1==person2);//True结构相同
Console.WriteLine(person1.Equals(person2));//True结构相同
Console.WriteLine(ReferenceEquals(person1,person2));//False,引用不同
//改变默认的记录!-->创建一个新的记录。
varperson3=person1with{Age=43};
Console.WriteLine(person3==person1);//False结构不同
//解构(Destruct)一个记录,将记录的属性提取为本地变量
var(name,age)=person3;
varperson4=newMutablePerson("Zhiminzhang",40);
person4.Age=43;
varperson5=newCitizen("ZhiminZhang",40,"China");
Console.WriteLine(person1==person5);
//记录类型也可以被继承
publicrecordCitizen(stringName,intAge,stringCountry):Person(Name,Age);
varcitizen=newCitizen("ZhiminZhang",40,"China");
Console.WriteLine(person1==citizen);//False类型不同;
- 优点:记录类型是轻量级的不可变类型,可以减少大量的代码,可以按照结构和引用进行比较;
- 缺点:需要实例化大量的对象;
如果要更加深入的学习记录类型,请查看微软的官方文档explorationofrecords。
模式匹配增强
C#9包含了一些新的模式匹配增强:
Typepatterns类型匹配,判断一个变量的类型
objectobj=newint();
vartype=objswitch{
string=>"string",
int=>"int",
_=>"obj"
};
Console.WriteLine(type);//int
Relationalpatterns关系匹配
//Relationalpatterns
varperson1=newPerson("ZhiminZhang",40);
varinRange=person1switch{
(_,<18)=>"lessthan18",
(_,>18)=>"greaterthan18",
(_,18)=>"18yearsold!"
};
Console.WriteLine(inRange);//greaterthan18
Conjunctiveandpatterns逻辑与匹配
//Andpattern
varperson1=newPerson("ZhiminZhang",40);
varageInRange=person1switch{
(_,<18)=>"lessthan18",
("ZhangZhimin",_)and(_,>=18)=>"ZhiminZhangisgreaterthan18"
};
Console.WriteLine(ageInRange);//ZhiminZhangisgreaterthan18
Disjunctiveorpatterns逻辑或匹配
//Orpattern
varperson1=newPerson("ZhiminZhang",40);
varageInRange=person1switch{
(_,<18)=>"lessthan18",
(_,18)or(_,>18)=>"18orgreater"
};
Console.WriteLine(ageInRange);//18orgreater
Negatednotpatterns逻辑非匹配
//Notpattern
varperson1=newPerson("ZhiminZhang",40);
varmeOrNot=person1switch{
not("ZhiminZhang",40)=>"Notme!",
_=>"Me:-)"
};
Console.WriteLine(meOrNot);//Me:-)
Parenthesizedpatterns带括号的优先级匹配
//Parenthesizedpatterns
varis10=newIsNumber(true,10);
varn10=is10switch{
((_,>1and<5)and(_,>5and<9))or(_,10)=>"10",
_=>"not10"
};
Console.WriteLine(n10);//10
注意,如果没有匹配到全部的情况,将会出现异常。
新的初始化表达式
在C#9.0中,当已创建对象的类型已知时,可以在new表达式中省略该类型。
publicclassMyClass{
privateList_observations=new();
}
Pointp=new(1,1);
Dictionarydict=new();
Point[]points={new(1,1),new(2,2),new(3,3)};
varlist=newList{
new(1,1),new(2,2),new(3,3)
};
- 优点:可以让代码更加简洁;
- 缺点:某些情况下会让代码更难理解;
目标类型条件表达式
可以隐式转换null值,在C#9.0中得到了增强。
voidTestMethod(int[]list,uint?u){
int[]x=list??newint[0];
varl=u??-1u;
}
GetEnumerator扩展
可以为任意类型添加一个GetEnumerator
publicstaticclassExtensions{
publicstaticIEnumeratorGetEnumerator(thisIEnumeratorenumerator)=>enumerator;
}
IEnumeratorenumerator=newCollection{
"A","B","C"
}.GetEnumerator();
foreach(variteminenumerator){
Console.WriteLine(item);
}
在本地函数上添加标记
允许在本地函数上添加标记。
staticvoidMain(string[]args){
[Conditional("DEBUG")]
staticvoidDoSomething([NotNull]stringtest){
Console.WriteLine("Doit!");
}
DoSomething("Doing!");
}
分部方法扩展
在C#9.0中,移除了分部方法的几个限制:
- 必须具有void返回类型。
- 不能具有out参数。
- 不能具有任何可访问性(隐式private)。
partialclassDoing{
internalpartialboolDoSomething(strings,outinti);
}
partialclassDoing{
internalpartialboolDoSomething(strings,outinti){
i=0;
returntrue;
}
}
静态lambda表达式
从C#9.0开始,可以将static修饰符添加到lambda表达式或匿名方法。静态lambda表达式类似于static局部函数:静态lambda或匿名方法无法捕获局部变量或实例状态。所述static可以防止意外捕获其他变量。
lambda表达式会捕获上下文的变量,不仅会有性能的问题,而且还可能出现错误,比如:
intnumber=0; FunctoString=()=>number.ToString();//number被自动捕获进toString函数中
可以在lambda表达式前添加static关键字,来解决这个问题:
intnumber=0; FunctoString=static()=>number.ToString();//这里无法再使用number;
模块初始化代码
可以使用ModuleInitializerAttribute为组件(assembly)定义初始化代码,当初始化/加载时执行,可以类比类的静态构造函数,但是是组件级别的,要求如下:
- 必须是静态的、无参数的、无返回值的方法;
- 不能是范型方法,也不能包含在范型类中;
- 不能是私有函数,必须是公开(public)或者内部(internal)的函数;
协变返回类型
协变返回类型为重写方法的返回类型提供了灵活性。覆盖方法可以返回从覆盖的基础方法的返回类型派生的类型。这对于记录和其他支持虚拟克隆或工厂方法的类型很有用。比如:
publicvirtualPersonGetPerson(){returnnewPerson();}
publicoverridePersonGetPerson(){returnnewStudent();}
在C#9.0中,可以在子类中返回更加详细的类型:
publicvirtualPersonGetPerson(){returnnewPerson();}
publicStudentPersonGetPerson(){returnnewStudent();}
原生整数类型
C#9添加了两个新的整数类型(nint和nunit),依赖宿主机以及编译设定。
nintnativeInt=55; Console.WriteLine(nint.MaxValue); //在x86平台上,输出为2147483647 //在x64平台上,输出为9223372036854775807
- 优点:可以更好的兼容原生API;
- 缺点:缺失平台无关性;
跳过本地初始化(SkipLocalInit)
在C#9.0中,可以使用SkipLocalsInitAttribute来告知编译器不要发射(Emit).localsinit标记。
[System.Runtime.CompilerServices.SkipLocalsInit]
staticunsafevoidDemoLocalsInit(){
intx;
//注意,x没有初始化,输出结果不确定;
Console.WriteLine(*&x);
}
- 优点:跳过本地初始化可以提升程序的性能;
- 缺点:性能的影响通常不大,建议只在极端情况下才使用这个;
函数指针
使用delegate*可以声明函数指针。
unsafeclassFunctionPointer{
staticintGetLength(strings)=>s.Length;
delegate*functionPointer=&GetLength;
}
publicvoidTest(){
Console.WriteLine(functionPointer("test"));//4;
}
以上就是C#9.0特性全面总结的详细内容,更多关于C#9.0特性的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。