⾯试必备之C#10语法特性总结
C# 10已与.NET 6、VS2022⼀起发布,本⽂按照.NET的发布顺序,根据微软官⽅⽂档整理C#中⼀些有趣的语法特性。
注:基于不同.NET平台创建的项⽬,默认⽀持的C#版本是不⼀样的。下⾯介绍的语法特性,会说明引⼊C#的版本,在使⽤过程中,需要注意使⽤C#的版本是否⽀持对应的特性。C#语⾔版本控制,可参考官⽅⽂档。
匿名函数
匿名函数是C# 2推出的功能,顾名思义,匿名函数只有⽅法体,没有名称。匿名函数使⽤delegate创建,可转换为委托。匿名函数不需要指定返回值类型,它会根据return语句⾃动判断返回值类型。
注:C# 3后推出了lambda表达式,使⽤lambda可以以更简洁的⽅式创建匿名函数,应尽量使⽤lambda来创建匿名函数。与lambda不同的是,使⽤delegate创建匿名函数可以省略参数列表,可将其转换为具有任何参数列表的委托类型。
// 使⽤delegate关键字创建,⽆需指定返回值,可转换为委托,可省略参数列表(与lambda不同)
Func<int, bool> func = delegate { return true; };
⾃动属性
从C# 3开始,当属性访问器中不需要其它逻辑时,可以使⽤⾃动属性,以更简洁的⽅式声明属性。编译时,编译器会为其创建⼀个仅可以通过get、set访问器访问的私有、匿名字段。使⽤VS开发时,可以通过snippet代码⽚段prop+2次tab快速⽣成⾃动属性。
// 属性⽼写法
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// ⾃动属性
public string Name { get; set; }
另外,在C# 6以后,可以初始化⾃动属性:
public string Name { get; set; } = "Louzi";
匿名类型
匿名类型是C# 3后推出的功能,它⽆需显⽰定义类型,将⼀组只读属性封装到单个对象中。编译器会⾃动推断匿名类型的每个属性的类型,并⽣成类型名称。从CLR的⾓度看,匿名类型与其它引⽤类型没什么区别,匿名类型直接派⽣⾃object。如果两个或多个匿名对象指定了顺序、名称、类型相同的属性,编译器会把它们视为相同类型的实例。在创建匿名类型时,如果不指定成员名称,编译器会把⽤于初始化属性的名称作为属性名称。
匿名类型多⽤于LINQ查询的select查询表达式。匿名类型使⽤new与初始化列表创建:
// 使⽤new与初始化列表创建匿名类型
var person = new { Name = "Louzi", Age = 18 };
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
// ⽤于LINQ
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
LINQ
C# 3推出了杀⼿锏功能,查询表达式,即语⾔集成查询(LINQ)。查询表达式以查询语法表⽰查询,由⼀组类似SQL的语法编写的⼦句组成。
查询表达式必须以from⼦句开头,必须以select或group⼦句结尾。在第⼀个from⼦句与最后⼀个select或group⼦句之间,可以包含:where、orderby、join、let、其它from⼦句等。
可以为SQL数据库、XML⽂档、ADO.NET数据集及实现了IEnumerable或IEnumerable接⼝的集合对象进⾏LINQ查询。
完整的查询包括创建数据源、定义查询表达式、执⾏查询。查询表达式变量是存储查询⽽不是查询结果,只有在循环访问查询变量后,才会执⾏查询。
可使⽤查询语法表⽰的任何查询都可以使⽤⽅法表⽰,建议使⽤更易读的查询语法。有些查询操作(如 Count 或 Max)没有等效的查询表达式⼦句,必须使⽤⽅法调⽤。可以结合使⽤⽅法调⽤和查询语法。
关于LINQ的详细⽂档,参见微软官⽅⽂档
// Data source.
int[] scores = { 90, 71, 82, 93, 75, 82 };
// Query Expression.
IEnumerable<int> scoreQuery = //query variable
from score in scores //required
where score > 80 // optional
orderby score descending // optional
select score; //must end with select or group
// Execute the query to produce the results
foreach (int testScore in scoreQuery)
{
Console.WriteLine(testScore);
}
Lambda
C# 3推出了很多强⼤的功能,如⾃动属性、扩展⽅法、隐式类型、LINQ,以及Lambda表达式。
创建Lambda表达式,需要在 => 左侧指定输⼊参数(空括号指定零个参数,⼀个参数可以省略括号),右侧指定表达式或语句块(通常两三条语句)。任何Lambda表达式都可以转换为委托类型,表达式Lambda语句还可以转换为表达式树(语句Lambda不可以)。
匿名函数可以省略参数列表,Lambda中不使⽤的参数可以使⽤弃元指定(C# 9)。
使⽤async和await,可以创建包含异步处理的Lambda表达式和语句(C# 5)。
从C# 10开始,当编译器⽆法推断返回类型时,可以在参数前⾯指定Lambda表达式的返回类型,此时参数必须加括号。
// Lambda转换为委托
Func<int, int> square = x => x * x;
// Lambda转换为表达式树
System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
/
/ 使⽤弃元指定不使⽤的参数
Func<int, int, int> constant = (_, _) => 42;
// 异步Lambda
var lambdaAsync = async () => await JustDelayAsync();
Console.WriteLine($"main thread id: {Thread.CurrentThread.ManagedThreadId}");
lambdaAsync();
static async Task JustDelayAsync()
{
await Task.Delay(1000);
Console.WriteLine($"JustDelayAsync thread id: {Thread.CurrentThread.ManagedThreadId}");
}
/
/ 指定返回类型,不指定返回类型会报错
var choose = object (bool b) => b ? 1 : "two";
扩展⽅法
writeline方法属于类扩展⽅法也是C# 3推出的功能,它能够向现有类型添加⽅法,且⽆需修改原始类型。扩展⽅法是⼀种静态⽅法,不过是通过实例对象语法进⾏调⽤,它的第⼀个参数指定⽅法操作的类型,⽤this修饰。编译器在编译为IL时会转换为静态⽅法的调⽤。
如果类型中具有与扩展⽅法相同名称和签名的⽅法,则编译器会选择类型中的⽅法。编译器进⾏⽅法调⽤时,会先在该类型的的实例⽅法中寻,不到再去搜索该类型的扩展⽅法。
最常见的扩展⽅法是LINQ,它将查询功能添加到现有的System.Collections.IEnumerable和System.Collections.Generic.IEnumerable 类型中。
为struct添加扩展⽅法时,由于是值传递,只能对struct对象的副本进⾏更改。从C# 7.2开始,可以为第⼀个参数添加ref修饰以进⾏引⽤传递,这样就可以对struct对象本⾝进⾏修改了。
static class MyExtensions
{
public static void OutputStringExtension(this string s) => Console.WriteLine($"output: {s}");
public static void OutputPointExtension(this Point p)
{
p.X = 10;
p.Y = 10;
Console.WriteLine($"output: ({p.X}, {p.Y})");
}
public static void OutputPointWithRefExtension(ref this Point p)
{
p.X = 20;
p.Y = 20;
Console.WriteLine($"output: ({p.X}, {p.Y})");
}
}
// class扩展⽅法
"Louzi".OutputStringExtension();
// struct扩展⽅法
Point p = new Point(5, 5);
p.OutputPointExtension(); // output: (10, 10)
Console.WriteLine($"original point: ({p.X}, {p.Y})"); // output: (5, 5)
p.OutputPointWithRefExtension(); // output: (20, 20)
Console.WriteLine($"original point: ({p.X}, {p.Y})"); // output: (20, 20)
隐式类型(var)
从C# 3开始,在⽅法范围内可以声明隐式类型变量(var)。隐式类型为强类型,由编译器决定类型。
var常⽤于调⽤构造函数创建对象实例时,从C# 9开始,这种场景也可以使⽤确定类型的new表达式:
// 隐式类型
var s = new List<int>();
// new表达式
List<int> ss = new();
注:当返回匿名类型时,只能使⽤var。
对象、集合初始化列表
从C# 3开始,可以在单条语句中实例化对象或集合并执⾏成员分配。
使⽤对象初始化列表,可以在创建对象时向对象的任何可访问字段或属性分配值,可以指定构造函数参数或忽略参数以及括号。
public class Person
{
// ⾃动属性
public int Age { get; set; }
public string Name { get; set; }
public Person() { }
public Person(string name)
{
Name = name;
}
}
var p1 = new Person { Age = 18, Name = "Louzi" };
var p2 = new Person("Sherilyn") { Age = 18 };
从C# 6开始,对象初始化列表不仅可以初始化可访问字段和属性,还可以设置索引器。
public class MyIntArray
{
public int CurrentIndex { get; set; }
public int[] data = new int[3];
public int this[int index]
{
get => data[index];
set => data[index] = value;
}
}
var myArray = new MyIntArray { [0] = 1, [1] = 3, [2] = 5, CurrentIndex = 0 };
集合初始化列表可以指定⼀个或多个初始值:
var persons = new List<Person>
{
new Person { Age = 18, Name = "Louzi" },
new Person { Age = 18, Name = "Sherilyn" }
};
内置泛型委托
.
NET Framework 3.5/4.0,分别提供了内置的Action和Func泛型委托类型。void返回类型的委托可以使⽤Action类型,Action的变体最多有16个参数。有返回值类型的委托可以使⽤Func类型,Func类型的变体最多同样16个参数,返回类型为Func声明中的最后⼀个类型参数。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论