文章目录
- 前言
- 名词解释
- 主要版本一览表
- 各版本主要特性一句话总结
-
- C# 1.0 (Visual Studio 2002, .Net Framework 1.0)
- C# 2.0 (Visual Studio 2005, .Net Framework 2.0)
- C# 3.0 (Visual Studio 2008, .Net Framework 3.0)
- C# 4.0 (Visual Studio 2010, .Net Framework 4)
- C# 5.0 (Visual Studio 2012, .Net Framework 4.5)
- C# 6.0 (Visual Studio 2015, .Net Framework 4.6)
- C# 7.0 (Visual Studio 2017, .Net Framework 4.7)
- C# 8.0 (Visual Studio 2019 .Net Framework 4.8)
- C# 9.0 (Visual Studio 2019, .Net 5.0)
- C# 10 (Visual Studio 2022, .Net 6.0)
- C# 11 (Visual Studio 2022, .Net 7.0)
- C# 12 (Visual Studio 2022,.Net 8.0)
- 日志
- 参考资料
前言
从2002年.Net平台发布开始,C#已经从1.0更新到了现在的8.0。目前网上有很多文章对这些版本的主要更新内容进行了总结,但是大都是内容的简单翻译和资料堆叠。本文根据自己的使用体验,在现有总结的基础上进行内容丰富细化,以便于读者能够对C#的语言特性有更加详细深刻的认识和理解。
自从上世纪90年代Java火了以后,微信就想做自己的Java,于是从1998年开始就制定了.NET计划,并于2002年正式发布。而C#就是.NET平台开发的首发语言,其主要参考对象为Java(熟悉这两门语言的朋友会发现两者有非常多的相似性),并结合VC6.0的界面设计方式、MFC的类库等为一体,整合而来。从C#1.0语言诞生开始,微软就不断为其添砖加瓦,丰富其功能。使其始终保持活跃的生命力。
名词解释
- C# 一门专门为 .NET 而推出的编程开发语言,对应Java
- .NET Framework C#的运行环境,对应 JDK。
- .Net 微软从C#9开始,将其真正开源做跨平台,包括5,6,7,8等版本。
- Visutal Studio 开发环境即IDE,对应 Eclipse。
主要版本一览表
C# 版本 | 发布时间 | 对应 .Net 框架版本 | 对应Visual Studio版本 |
---|---|---|---|
C# 1.0 | 2002.01 | .NET Framework 1.0/1.1 | Visual Studio 2002 |
C# 2.0 | 2005.11 | .NET Framework 2.0 | Visual Studio 2005 |
C# 3.0 | 2007.11 | .NET Framework 3.0/3.5 | Visual Studio 2008 |
C# 4.0 | 2010.04 | .NET Framework 4.0 | Visual Studio 2010 |
C# 5.0 | 2012.08 | .NET Framework 4.5 | Visual Studio 2012 |
C# 6.0 | 2015.07 | .NET Framework 4.6 | Visual Studio 2015 |
C# 7.0 | 2017.03 | .NET Framework 4.7.1~4.7.3 | Visual Studio 2017 |
C# 8.0 | 2019.09 | .NET Framework 4.8 | Visual Studio 2019 |
C# 9.0 | 2020.11 | .NET 5 (跨平台统一命名) | Visual Studio 2020 |
C# 10.0 | 2021.11 | .NET 6 | Visual Studio 2022 |
C# 11.0 | 2022.11 | .NET 7 | Visual Studio 2022 |
C# 12.0 | 2023.11 | .NET 8 | Visual Studio 2022 |
我们在新建项目的时候所要选择的就是.NET Framework的版本号。如下图所示:
各版本主要特性一句话总结
C# 1.0 (Visual Studio 2002, .Net Framework 1.0)
回过头来看看,C#1.0版本看起来很像Java。当时的设计目标是成为一种“简单,现代,通用的面向对象语言”。 当时,看起来像Java意味着它实现了那些早期的设计目标。但如果现在回顾一下C#1.0,你会发现它的功能很弱:缺乏内置的异步功能和一些我们认为理所当然的泛型功能。 比如,没有泛型、LINQ、Lambda表达式。与今天相比,C#版本1.0看起来被剥夺了功能。 你会发现自己编写了一些冗长的代码。 但是,你必须从某个地方开始。这个版本的特性就不列出来了,可以理解为最开始的基本功能的C#。如果没有后面微软的不断更新,则不会有C#的今天,因为它很快就会被更多的语言替代而被人遗忘。
C# 2.0 (Visual Studio 2005, .Net Framework 2.0)
微软在 Studio 2005 中发布了C#2.0。在此版本中,主要引入了很多新特性(比如非常重要的泛型),以帮助开发人员以更通用的方式编写应用程序代码。 以下是C#2.0引入的新功能:
- Generics
泛型,如经常使用的List<String> list = new List<String>();
- Partial types
分部类型,可以将类、结构、接口等类型定义拆分到多个文件中,如在窗体设计类中,就有两个文件 Form.cs 和 Form.designer.cs 将业务逻辑和界面设计分离 - Anonymous methods
匿名方法,即可以在不定义函数名称和参数列表的情况直接定义函数,主要的使用是方便调用代理。 - Iterators
迭代器,即foreach
语法 - Nullable types
可以为Null的类型,该类可以是其它值或者null,便于空值的判断 - Getter/setter separate accessibility
属性访问和设置权限分享,即可以分别设置getter 和 setter 的可访问性。 - Method group conversions (delegates)
方法组转换,可以将声明委托代表一组方法,隐式调用 - Co- and Contra-variance for delegates and interfaces
委托、接口的协变和逆变,主要用于扩大委托的定义范围,具体参见文章:快速理解C#委托中的协变与逆变。 - Static classes
静态类,即可以定义一个类为 static,从而保证其唯一性 - Delegate inference
委托推断,允许将方法名直接赋给委托变量,从而简化操作
C# 3.0 (Visual Studio 2008, .Net Framework 3.0)
- Implicitly typed local variables
可以使用var关键字,如var s = "tom"
,从而不用在定义时明确指定数据类型,方便使用。 - Object and collection initializers 对象和集合初始化器
对象初始化器:Person tom = new Person(){ ID = 0, Name = "Tom"};
集合初始化器:List<string> names = { "Tom", "Jack", "Lucy"};
- Auto-Implemented properties 自动属性,自动生成属性方法,声明更简洁
示例:public string Name{get;set;}
- Anonymous types 匿名类型
示例:先定义var tom= new { ID = 0, Name = "Tom" };
再使用Console.WriteLine($"Name = {tom.Name}");
。 - Extension methods:扩展方法
即可以给一个已经封装好的类添加新的方法(注意:不是派生)。 - Query Expressions
查询表达式提供了两种全新的表达语言:Lambda 表达式
和表达式树(Expression trees)
。其中,表达式树是以树形数据结构表示代码,是一种新数据类型,具体参见:表达式树。表达式树存在的主要目的就是可以根据需求的不同从而动态修改表达式树中的表达式以便生成可在非C#环境执行的代码。 - Partial methods
在C#2.0中引入了类,其目的是实现前台和后台的分离。但是在实际应用中,有时候一些方法仍然需要前后台的人员进行共同合作才能解决。为了处理这个问题,引入了分部方法,即可以在一个部分类中引入声明,在另一个相同的分部类中进行定义。如下所示:
定义部分:public partial class Form1: Form { partial void method();}
实现部分:partial class Form1: Form { partial void method (){ Console.WriteLine("Partial method");}
即便没有实现,编译的时候也可以正常通过,以方便设计和开发人员。
C# 4.0 (Visual Studio 2010, .Net Framework 4)
- Dynamic binding
C#3.0 提供了var
关键字,可以自动推断数据类型,4.0 又提供了一个看上去类似的dynamic
。这个关键字也是用于定义对象,并且也不需要知道数据类型,即可调用对象的方法,但是区别在于var
是根据上下文推断数据类型,然后再调用,而dynamic
是不对类型进行检测,即便对象是object
类型,调用一个未知的方法也不会报错。因为它会在程序运行时,真正执行到所在行的时候才会进行判断,如果对象中有调用的方法,则正常调用,否则就会报错,具体可以参见: C#中dynamic的正确用法。 - Named and optional arguments
中文翻译为:具名参数和可选参数。
可选参数:即为参数加上默认值,如void show(string msg="info"){ Console.WriteLine(msg);}
,在调用时可以不写参数,即:show()
,此时msg
的值为info
。
具名函数:若可选参数的函数定义为:int add(int x = 1, int y)
,那么在调用时第1个省略后就会报错,所以可以使用具名函数调用,调用方法为:add(y:5);
,此时x
为默认值,y
为 5。
即具体可以参见这篇文章:C#中的 具名参数 和 可选参数。 - Generic co- and contravariance
泛型的协变和逆变,原理同C#2.0委托、接口的协变和逆变。 - Embedded interop types (“NoPIA”)
中文翻译为:嵌入互操作类型。表示嵌入类型信息,增加引用COM组件程序的中立性,主要用于COM模块的调用。具体可以参阅这篇文章:C# 嵌入互操作类型。
C# 5.0 (Visual Studio 2012, .Net Framework 4.5)
- Asynchronous methods
用于多线程编程,可以异步调用方法,请参阅:C#异步编程方式以及示例。 - Caller info attributes
调用方信息特性。主要作用是可以显示调用函数的名称、所在文件和所在行的信息。基主要目的是为了便于调试。具体可以参见这篇文章:http://www.itstrike.cn/Question/99a193ca-e99a-45ba-99f1-1f38017313db - 其他
实际上5.0的新特性肯定不会只有两条,但是了由于特性不是很明显,于是就不再详细介绍,具体可以参见:C# 5.0五大新特性。
C# 6.0 (Visual Studio 2015, .Net Framework 4.6)
- Import of static type members into namespace
用于简化书写。在引用类名后,可以直接使用类的静态成员,比如在使用using static System.String;
后,可以将String.IsNullOrEmpty(str)
可以直接写成IsNullOrEmpty(str)
。 - Exception filters
异常过滤器,即在异常的时候可以进行删除,比如定义一个变量int level;
,那么在可以使用这样的语法:try{
doSomething();
} catch(Exception e) when (level == 10){
ProcessException(e);
}
这样,只有在level == 10
的时候,才会进行异常处理。 - Await in catch/finally blocks
支持在catch/finally语句块使用await语句进行异常调用 。 - Auto property initializers
自动属性的初始化。这个非常好用,可以在定义属性时直接赋值,比如public string Name { get; set; } = "no name";
。 - Default values for getter-only properties
即给只读属性设置默认值,如public string Name { get; } = "no name";
。 - Expression-bodied members
表达式主体成员指的是仅以表达式定义的成员。举例来说:一个类中有个public string Name {get; set}
属性,在ToString()
方法重写的时候,我们需要以下格式:public override string ToString() { return $"{Name}".Trim();}
现在优化后,同样也可以使用 Lambda 的操作符号=>
进行简化,简化后为:public override string ToString() => $"{Name}".Trim();
注:当代码有多行的时候,使用一对花括号分隔,但是右花括号后要以分号结束。 - Null propagator (null-conditional operator, succinct null checking)
Null条件操作符,用法有两种:
1)非空时才调用,如obj?.ToString();
,如果obj
不为空,则执行obj.ToString();
,否则跳过不执行此行。相当于if(obj != null) obj.ToString();
。
2)string s = obj ?? obj.ToString();
,作用同上。 - String interpolation
字符串插入拼接。用于简化字符串的拼接。这个超级好用,比如说:string s1 = "John", s2 = "Corner";
如果想得到"Jone Corner"
,原来的方法要么拼接,即s1 + " " + s2
,要么使用String.Format
。新方法很简单:$"{s1} {s2}"
,实际上就是对String.Format
的简化,而且在{}
中非常强大,可以所有常规的表达式,甚至直接支持双引号,比如:$"{DateTime.Now.ToString("yyyy/MM/dd")}"
输出为2019/08/28
。 - nameof operator
新加入的关键字nameof
,用于返回方法、属性、变量的名称,如string name = nameof(String.Format);
,返回Format
。很多时候,我们如果直接使用字符串,当名称变更的时候会不好用,而使用了nameof
的时候,可以直接与方法、属性、变量等名称关联,即便这个变量的名称发生变化了,使用nameof
以后,也会跟着一起变化,这个在动态编辑时很有用。 - Dictionary initializer
字典初始化。格式如下所示:var moreNumbers = new Dictionary<int, string>
{
{19, "nineteen" },
{23, "twenty-three" },
{42, "forty-two" }
};
C# 7.0 (Visual Studio 2017, .Net Framework 4.7)
- Out variables
可以在out后面直接声明变量,例如前TryParse需要先定义再使用的两行的代码,现在一行即可:int.TryParse(s, out int num)
,从而简化语法。 - Pattern matching
数据类型转换的语法糖。可以在类型判断同时定义符合的变量。如变量object obj
,判断其是否是int,并进行求和。
之前的写法:if (obj is int) sum += (int)obj;
C# 7的写法:if (item is int val) sum += val;
由以可见,在判断时自动定义了转换好的值,从而简化代码。除了 if 语句,在 switch 语句中也同样适用。 - Tuples
元组改进,可以为无组添加名称,用法:
方式1:定义:(int one, int two) tuple = (1, 2);
使用:WriteLine($"first:{tuple.one}, second:{tuple.two}");
。
方式2:定义:var tuple2 = (one: 1, two: 2);
使用:WriteLine($"first:{tuple2.one}, second:{tuple2.two}");
。 - Deconstruction
可以从一个类的构造函数中提取相应的数据,比如:var (Name, Age) = new Person(Name, Age);
。 - Local Functions
在函数中定义子函数,子函数体放在 return 后面。这个相关于C++中的inline,用于提高效率,一般非性能场合不推荐使用,因为它不仅影响了程序的阅读性,而且也不利于代码管理。 - Binary Literals
二进制使用0b
表示,如var one= 0b000_0001
; - Digit Separators
在数字中使用下划线作为分隔符,这个是为了增强代码的可读性。例如,1000000,可以写成 1_000_000。 - Ref returns and locals
对ref引用进行了加强,现在可以引用一个方法中的局部变量,语法是:在方法定义前加上 ref,同时在返回时也加上ref,如定义函数ref int GetDataRef(int[] data, int index){ return ref data[index];}
,则ref int num = ref GetDataRef(new data[]{1,2,3}, 0);
返回,对数组中第0个元素的引用。 - Generalized async return types
如名称所示,现在泛型也支持async。 - More expression-bodied members
进一步丰富C#6中的表达式成员的功能,允许构造器、解析器、属性可以使用表达式作为body。比如属性:public string Name{ get => name; set => name = value ?? "noname";}
- Throw expressions
Throw可以抛出表达式中。如果表达式成立,则抛出表达式的值,类型为System.Exception,如果值为 null,则抛出System.NullReferenceException。
更多:下面是 7.1-7.3在更新的一些属性,仅供参考。
- C# 7.1 特征 (Visual Studio 2017 version 15.3)
Async main:在main方法用async方式
Default expressions:引入新的字面值default
Reference assemblies:
Inferred tuple element names:
Pattern-matching with generics: - C# 7.2 特征 (Visual Studio 2017 version 15.5)
Span and ref-like types
In parameters and readonly references
Ref conditional
Non-trailing named arguments
Private protected accessibility
Digit separator after base specifier - C# 7.3 (Visual Studio 2017 version 15.7)
重载解析
泛型约束:枚举、委托和非托管
隐藏字段的Attribute
元组比较(==和!=)
栈分配Span
可重新赋值的Ref局部变量
初始化器中的表达式变量
栈分配数组
C# 8.0 (Visual Studio 2019 .Net Framework 4.8)
参考网页 官网:C# 8.0 中的新增功能
- Readonly 成员 参考
添加了readonly关键字的函数,不能修改类内成员,从而保证数据的只读性。如以下代码会报错。
public struct Vector2{public float x,y; public readonly float GetLengthIllegal(){var tmp = MathF.Sqrt(LengthSquared); x = tmp; // Compiler error, cannot write xy = tmp; // Compiler error, cannot write y return tmp;}
}
-
默认接口方法
-
模式匹配增强功能:
-
Switch 表达式
-
属性模式
-
元组模式
-
位置模式
-
Using 声明
-
静态本地函数
-
可处置的 ref 结构
-
可为空引用类型 ref
增加了可空类型,使用type?
表示,如string?
表示可空字符串。好处就是string?
可能为空,而string
就一定不为空,这样可以省去判断。 -
异步流
-
异步可释放
-
索引和范围
-
Null 合并赋值
-
非托管构造类型
-
嵌套表达式中的 Stackalloc
-
内插逐字字符串的增强功能
参考:官网
C# 9.0 (Visual Studio 2019, .Net 5.0)
参考网页 官网:C# 9.0 中的新增功能
- 记录
- 仅限 Init 的资源库
- 顶级语句
- 模式匹配增强功能
- 性能和互操作性
- 本机大小的整数
- 函数指针
- 禁止发出 localsinit 标志
- 调整和完成功能
- 目标类型的 new 表达式
- static 匿名函数
- 目标类型的条件表达式
- 协变返回类型
- 扩展 GetEnumerator 支持 foreach 循环
- Lambda 弃元参数
- 本地函数的属性
- 支持代码生成器
- 模块初始值设定项
- 分部方法的新功能
C# 10 (Visual Studio 2022, .Net 6.0)
参考网页 官网:C# 10.0 中的新增功能
- 记录结构
- 结构类型的改进
- 内插字符串处理程序
- 指令
- 文件范围的命名空间声明
- 扩展属性模式
- 对 Lambda 表达式的改进
- 可使用 内插字符串
- 记录类型可密封
- 改进型明确赋值
- 在同一析构中可同时进行赋值和声明
- 可在方法上使用 属性
- CallerArgumentExpression 属性
- 增强的 pragma
C# 11 (Visual Studio 2022, .Net 7.0)
主要功能特性
- 原始字符串字面量
注: 左引号之后、右引号之前的换行符不包括在最终内容中
string longMessage = """This is a long message.It has several lines.Some are indentedmore than others.Some should start at the first column.Some have "quoted text" in them.""";
- 泛型数学支持
- 泛型属性
- UTF-8 字符串字面量
- 字符串内插表达式中的换行符
- 列表模式
- 文件本地类型
- 必需的成员
- 自动默认结构
- 常量 string 上的模式匹配 Span
- 扩展的 nameof 范围
- 数值 IntPtr
- ref 字段和 scoped ref
- 改进了方法组向委托的转换
- 警告波
PS. 从 .NET 6.0.200 SDK 或 Visual Studio 2022 版本 17.1 开始,可以试用 C# 中的预览功能。
参考网页 官网:C# 11.0 中的新增功能
C# 12 (Visual Studio 2022,.Net 8.0)
主构造函数
默认 Lambda 参数
任何类型的别名
内联数组
侦听器
参考: 官网链接
日志
- 2023/08/01 更新部分错误并优化文档
- 2022/05/02 添加 11.0
参考资料
- Expression-bodied 成员(C# 编程指南), 微软官方, 2017.
- VS2017十五项新功能体验, Leo_wl, 2017.
- 带你玩转Visual Studio——VS2015的新功能和特性
- 详解C#7.0新特性, CNXY, 2017-10.
- C# Version History, TutorialsTeacher.com, 2019.
- 文章链接