avatar

目录
一文看懂C#各版本新功能+代码演示

代码演示C#各版本新功能

C#各版本新功能其实都能在官网搜到,但很少有人整理在一起,并通过非常简短的代码将每个新特性演示出来。

  • 代码演示C#各版本新功能
  • C# 2.0版 - 2005
    • 泛型
    • 分部类型
    • 匿名方法
    • 可以为null的值类型
    • 迭代器
    • 协变和逆变
  • C# 3.0版 - 2007
    • 自动实现的属性
    • 匿名类型
    • 查询表达式(LINQ)
    • Lambda表达式
    • 表达式树
    • 扩展方法
    • var
    • 分部方法
    • 对象和集合初始值设定项
  • C# 4.0版 - 2010
    • dynamic
    • 命名参数/可选参数
    • 泛型中的协变和逆变
    • 类型等效、内置互操作类型
  • C# 5.0版 - 2012
    • async/await
    • 调用方信息
  • C# 6.0版 - 2015
    • 静态导入
    • 异常筛选器
    • 自动初始化表达式
    • Expression-bodied 函数成员
    • Null传播器
    • 字符串内插
    • nameof表达式
    • 索引初始值设定项
  • C# 7.0版本 - 2017
    • out变量
    • 元组和析构函数
    • 模式匹配
    • 本地函数
    • 更多的expression-bodied成员
    • Ref 局部变量和返回结果
    • 弃元
    • 二进制文本和数字分隔符
    • throw表达式
  • C# 8.0 版 - 2019
    • Readonly 成员
    • 默认接口方法
    • 模式匹配增强
    • 属性模式
    • Tuple模式
    • 位置模式
    • switch表达式
    • using声明
    • 静态本地函数
    • 异步流
    • 索引和范围
    • Null合并赋值
    • 非托管构造类型
    • 嵌套表达式中的 stackalloc
  • 附录/总结

C# 2.0版 - 2005

泛型

Java中的泛型不支持值类型,且会运行时类型擦除,这一点 .NET更优秀。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// Declare the generic class.
public class GenericList
{
public void Add(T input) { }
}
class TestGenericList
{
private class ExampleClass { }
static void Main()
{
// Declare a list of type int.
GenericList list1 = new GenericList();
list1.Add(1);

// Declare a list of type string.
GenericList list2 = new GenericList();
list2.Add("");

// Declare a list of type ExampleClass.
GenericList list3 = new GenericList();
list3.Add(new ExampleClass());
}
}

分部类型

拆分一个类、一个结构、一个接口或一个方法的定义到两个或更多的文件中是可能的。 每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组合起来。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14

public partial class Employee
{
public void DoWork()
{
}
}

public partial class Employee
{
public void GoToLunch()
{
}
}

匿名方法

Code
1
2
3

Func sum = delegate (int a, int b) { return a + b; };
Console.WriteLine(sum(3, 4)); // output: 7

可以为null的值类型

Code
1
2
3
4
5
6
7
8
9
10
11

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable type:
int?[] arr = new int?[10];

迭代器

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

static void Main()
{
foreach (int number in SomeNumbers())
{
Console.Write(number.ToString() + " ");
}
// Output: 3 5 8
Console.ReadKey();
}

public static System.Collections.IEnumerable SomeNumbers()
{
yield return 3;
yield return 5;
yield return 8;
}

协变和逆变

在 C# 中,协变和逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。 协变保留分配兼容性,逆变则与之相反。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

// Assignment compatibility.
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type.
object obj = str;

// Covariance.
IEnumerable strings = new List();
// An object that is instantiated with a more derived type argument
// is assigned to an object instantiated with a less derived type argument.
// Assignment compatibility is preserved.
IEnumerable objects = strings;

// Contravariance.
// Assume that the following method is in the class:
// static void SetObject(object o) { }
Action actObject = SetObject;
// An object that is instantiated with a less derived type argument
// is assigned to an object instantiated with a more derived type argument.
// Assignment compatibility is reversed.
Action actString = actObject;

C# 3.0版 - 2007

自动实现的属性

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

// This class is mutable. Its data can be modified from
// outside the class.
class Customer
{
// Auto-implemented properties for trivial get and set
public double TotalPurchases { get; set; }
public string Name { get; set; }
public int CustomerID { get; set; }

// Constructor
public Customer(double purchases, string name, int ID)
{
TotalPurchases = purchases;
Name = name;
CustomerID = ID;
}

// Methods
public string GetContactInfo() { return "ContactInfo"; }
public string GetTransactionHistory() { return "History"; }

// .. Additional methods, events, etc.
}

class Program
{
static void Main()
{
// Intialize a new object.
Customer cust1 = new Customer(4987.63, "Northwind", 90108);

// Modify a property.
cust1.TotalPurchases += 499.99;
}
}

匿名类型

Code
1
2
3
4
5
6

var v = new { Amount = 108, Message = "Hello" };

// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and n .
Console.WriteLine(v.Amount + v.Message);

查询表达式(LINQ)

我们通常认为 C# 版本的杀手锏是查询表达式,也就是语言集成查询 (LINQ)。
LINQ 的构造可以建立在更细微的视图检查表达式树、Lambda 表达式以及匿名类型的基础上。 不过无论如何 C# 3.0 都提出了革命性的概念。 C# 3.0 开始为 C# 转变为面向对象/函数式混合语言打下基础。
具体来说,你现在可以编写 SQL 样式的声明性查询对集合以及其他项目执行操作。

Lambda表达式

Code
1
2
3
4
5
6


Func square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

表达式树

扩展方法

扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。

var

Code
1
2
3

var i = 10; // Implicitly typed.
int i = 10; // Explicitly typed.

分部方法

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

namespace PM
{
partial class A
{
partial void OnSomethingHappened(string s);
}

// This part can be in a separate file.
partial class A
{
// Comment out this method and the program
// will still compile.
partial void OnSomethingHappened(String s)
{
Console.WriteLine("Something happened: {0}", s);
}
}
}

对象和集合初始值设定项

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public class Cat
{
// Auto-implemented properties.
public int Age { get; set; }
public string Name { get; set; }

public Cat()
{
}

public Cat(string name)
{
this.Name = name;
}
}

C# 4.0版 - 2010

动态绑定

命名参数/可选参数

Code
1
2
3
4
5

PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);

public void ExampleMethod(int required, string optionalstr = "default string",
int optionalint = 10)

泛型中的协变和逆变

Code
1
2
3
4
5
6
7

IEnumerable d = new List();
IEnumerable b = d;

Action b = (target) => { Console.WriteLine(target.GetType().Name); };
Action d = b;
d(new Derived());

类型等效性和嵌入的互操作类型

C# 5.0版 - 2012

异步编程

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

private DamageResult CalculateDamageDone()
{
// Code omitted:
//
// Does an expensive calculation and returns
// the result of that calculation.
}

calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone()
// performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};

调用方信息

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public void DoProcessing()
{
TraceMessage("Something happened.");
}

public void TraceMessage(string message,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
System.Diagnostics.Trace.WriteLine("message: " + message);
System.Diagnostics.Trace.WriteLine("member name: " + memberName);
System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}

// Sample Output:
// message: Something happened.
// member name: DoProcessing
// source file path: c:\Visual Studio Projects\CallerInfoCS\CallerInfoCS\Form1.cs
// source line number: 31

C# 6.0版 - 2015

静态导入

Code
1
2

using static System.Math;

异常筛选器

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public static async Task MakeRequest()
{
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.AllowAutoRedirect = false;
using (HttpClient client = new HttpClient(webRequestHandler))
{
var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
try
{
var responseText = await stringTask;
return responseText;
}
catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
{
return "Site Moved";
}
}
}

自动初始化表达式

Code
1
2

public ICollection Grades { get; } = new List();

Expression-bodied 函数成员

Code
1
2

public override string ToString() => $"{LastName}, {FirstName}";

Null传播器

Code
1
2

var first = person?.FirstName;

字符串内插

Code
1
2
3

public string GetGradePointPercentage() =>
$"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

nameof表达式

nameof 表达式的计算结果为符号的名称。 每当需要变量、属性或成员字段的名称时,这是让工具正常运行的好办法。 nameof 的其中一个最常见的用途是提供引起异常的符号的名称:

Code
1
2
3

if (IsNullOrWhiteSpace(lastName))
throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

索引初始值设定项

使集合初始化更容易的另一个功能是对 Add 方法使用扩展方法 。 添加此功能的目的是进行 Visual Basic 的奇偶校验。 如果自定义集合类的方法具有通过语义方式添加新项的名称,则此功能非常有用。

C# 7.0版 - 2017

out变量

Code
1
2
3
4
5

if (int.TryParse(input, out int result))
Console.WriteLine(result);
else
Console.WriteLine("Could not parse input");

元组和析构函数

Code
1
2
3

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

析构函数应该类似C++中的析构函数,在实例回收时执行?

模式匹配

Code
1
2
3

if (input is int count)
sum += count;

本地函数

也可用lambda代替

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

public static IEnumerable AlphabetSubset3(char start, char end)
{
if (start < 'a' || start > 'z')
throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
if (end < 'a' || end > 'z')
throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

if (end <= start)
throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

return alphabetSubsetImplementation();

IEnumerable alphabetSubsetImplementation()
{
for (var c = start; c < end; c++)
yield return c;
}
}

更多的expression-bodied成员

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// Expression-bodied get / set accessors.
public string Label
{
get => label;
set => this.label = value ?? "Default label";
}

Ref 局部变量和返回结果

此功能允许使用并返回对变量的引用的算法,这些变量在其他位置定义。 一个示例是使用大型矩阵并查找具有某些特征的单个位置。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public static ref int Find(int[,] matrix, Func predicate)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if (predicate(matrix[i, j]))
return ref matrix[i, j];
throw new InvalidOperationException("Not found");
}


ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

弃元

二进制文本和数字分隔符

引发表达式

C# 8.0版 - 2019

Readonly 成员

Code
1
2
3

public readonly override string ToString() =>
$"({X}, {Y}) is {Distance} from the origin";

默认接口方法

在更多位置中使用更多模式

* switch表达式

Code
1
2
3
4
5
6
7
8
9
10
11
12
13

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00),
Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00),
Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF),
Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
_ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

* 属性模式

Code
1
2
3
4
5
6
7
8
9
10

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
location switch
{
{ State: "WA" } => salePrice * 0.06M,
{ State: "MN" } => salePrice * 0.75M,
{ State: "MI" } => salePrice * 0.05M,
// other cases removed for brevity...
_ => 0M
};

* 元组模式

Code
1
2
3
4
5
6
7
8
9
10
11
12

public static string RockPaperScissors(string first, string second)
=> (first, second) switch
{
("rock", "paper") => "rock is covered by paper. Paper wins.",
("rock", "scissors") => "rock breaks scissors. Rock wins.",
("paper", "rock") => "paper covers rock. Paper wins.",
("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
("scissors", "rock") => "scissors is broken by rock. Rock wins.",
("scissors", "paper") => "scissors cuts paper. Scissors wins.",
(_, _) => "tie"
};

* 位置模式

Code
1
2
3
4
5
6
7
8
9
10
11

static Quadrant GetQuadrant(Point point) => point switch
{
(0, 0) => Quadrant.Origin,
var (x, y) when x > 0 && y > 0 => Quadrant.One,
var (x, y) when x < 0 && y > 0 => Quadrant.Two,
var (x, y) when x < 0 && y < 0 => Quadrant.Three,
var (x, y) when x > 0 && y < 0 => Quadrant.Four,
var (_, _) => Quadrant.OnBorder,
_ => Quadrant.Unknown
};

using声明

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

static int WriteLinesToFile(IEnumerable lines)
{
using var file = new System.IO.StreamWriter("WriteLines2.txt");
// Notice how we declare skippedLines after the using statement.
int skippedLines = 0;
foreach (string line in lines)
{
if (!line.Contains("Second"))
{
file.WriteLine(line);
}
else
{
skippedLines++;
}
}
// Notice how skippedLines is in scope here.
return skippedLines;
// file is disposed here
}

静态本地函数

Code
1
2
3
4
5
6
7
8
9

int M()
{
int y = 5;
int x = 7;
return Add(x, y);

static int Add(int left, int right) => left + right;
}

异步流

Code
1
2
3
4
5
6
7
8
9

public static async System.Collections.Generic.IAsyncEnumerable GenerateSequence()
{
for (int i = 0; i < 20; i++)
{
await Task.Delay(100);
yield return i;
}
}

索引和范围

感觉类似python中的切片器,-^代替了

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var words = new string[]
{
// index from start index from end
"The", // 0 ^9
"quick", // 1 ^8
"brown", // 2 ^7
"fox", // 3 ^6
"jumped", // 4 ^5
"over", // 5 ^4
"the", // 6 ^3
"lazy", // 7 ^2
"dog" // 8 ^1
}; // 9 (or words.Length) ^0

var quickBrownFox = words[1..4];
var lazyDog = words[^2..^0];
var allWords = words[..]; // contains "The" through "dog".
var firstPhrase = words[..4]; // contains "The" through "fox"
var lastPhrase = words[6..]; // contains "the", "lazy" and "dog"

Null合并赋值

Code
1
2
3
4
5
6
7
8
9
10

List numbers = null;
int? i = null;

numbers ??= new List();
numbers.Add(i ??= 17);
numbers.Add(i ??= 20);

Console.WriteLine(string.Join(" ", numbers)); // output: 17 17
Console.WriteLine(i); // output: 17

非托管构造类型

与任何非托管类型一样,可以创建指向此类型的变量的指针,或针对此类型的实例在堆栈上分配内存块

Code
1
2
3
4
5
6
7

Span> coordinates = stackalloc[]
{
new Coords { X = 0, Y = 0 },
new Coords { X = 0, Y = 3 },
new Coords { X = 4, Y = 0 }
};

嵌套表达式中的 stackalloc

Code
1
2
3
4

Span numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6 ,8 });
Console.WriteLine(ind); // output: 1

原文链接:Akiya Xiao:《C# 各版本新功能列表》
参考资料:《C#发展历史 - C#指南》

文章作者: HJY
文章链接: https://hjy-dev.github.io/2020/03/01/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82C-%E5%90%84%E7%89%88%E6%9C%AC%E6%96%B0%E5%8A%9F%E8%83%BD-%E4%BB%A3%E7%A0%81%E6%BC%94%E7%A4%BA/
版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明来自 Kiven Blog
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论