由于您能够用,而且也是您的最佳选择!之因此可用,是由于 C# 可以很好地在 Mac、Linux、Android 和 iOS 上运行(对了,还有 Windows);它能够在您最喜好的编辑器上运行;它在一个稳定的企业级平台上通过了充分的时间验证;最为重要的是:它是彻底开源的!之因此是您的最佳选择,是由于 C# 是编程语言创新方面的领导者,是原生跨平台移动应用程序的最佳选择,而且还有不少的优势超乎您的想象。在本次 GOTO Copenhagen 2016 大会讲演上,Mads Torgersen 邀请您一块儿来探索 C# 的核心,探究为何它仍然散发着活力,并探寻将来 C# 的发展趋势。java
概述 c++
我是 Mads Torgersen,就任于微软的 C# 部门。我如今年纪大了,所以我随身都穿着这件 T恤,上面印着我正在负责的项目和语言名称,以防我忘掉它们。这里我想谈一谈 C#,为何我要推荐用它来做为你们的首选编程语言呢(即便到目前为止您尚未接触过 C#)。程序员
Stack Overflow - 最受欢迎和喜好的技术
编程
Stack Overflow 每一年都会进行一次调查,询问不少开发者们都关心的问题(固然,在不少方面这些问题都是很具备倾向性,是很不科学的)。您必须在 Stack Overflow 上才能参与。
数组
C# 是一门被普遍使用的编程语言(排行第四,排行前三当中有一门实际上并不属于编程语言——我说的不是 JavaScript,我说的是 SQL)。能够看出,C# 是一门主流语言。
安全
他们一样还问开发者们:是否还想继续使用目前正在用的语言,并让人们投票出他们最喜好的技术。C# 一样也在这个列表当中。这说明人们都很喜欢 C# 这门语言。此外还有其余人们也喜欢的语言,可是您还能够注意到,这些语言中的大部门要么就是受众较少,要么就是很是专业化,不少都是某种狂热信仰的一部分了。在这两个列表当中,只有少数才是用途普遍、受人们高度喜好的。很高兴能看到 C# 位于这个列表的三大最受欢迎的技术之一,其中两个是编程语言,而且* Python 也在这里面*。
服务器
咱们不断思考咱们的所做所为,怎样才多是正确的呢,怎样才能让咱们在多年之后仍然喜欢它。彷佛并非全部人都用过 C#,由于不少人所在的公司已经有 10 年多的历史了,里面存在了不少的遗留代码。目前 C# 仍然保持着活力,咱们但愿它能将这份活力保持下去。咱们一样也有各式各样的想法,而这驱动了 C# 的演进。
数据结构
咱们很是渴望去演进 C#。若是您看过现代语言的演变进程的话(从少到多),就会明白咱们积极保持语言现代化的目的所在了。做为参与编程语言演变的一份子,咱们有些时候是推进者,有些时候是跟随者,不管如何,咱们都试图让 C# 成为程序员们现在的绝佳选择之一。咱们不该该搞所谓的「限定」,只局限于某几个平台,由于过去十年当中就有人这么作了,结果可想而知。
架构
我还想提一提 F#,由于这至关因而咱们的姊妹语言,它很是受欢迎,由于它很轻巧、也很强大。F# 是一门功能强大的语言,咱们在与 F# 团队的合做当中获益良多,而且它也给咱们提供了不少设计灵感。
并发
(说明:F#是由微软发展的为微软.NET语言提供运行环境的程序设计语言。它是基于Ocaml的,而Ocaml是基于ML函数程序设计语言的。函数编程是解决许多棘手问题的最好方法,可是,纯函数编程并不适合常规编程。所以,函数编程语言逐渐吸取了命令式、面向对象的编程模式,不只保持了函数编程范式,同时也混合了其余须要的功能,使函数编程编写各类类型的程序都很容易。F# 就是这种尝试的成功表明,比其余函数编程语言作得更多。F#主要是为了解决特定的某些复杂问题,因此自己定位使得VS没有提供F#的ASP.NET/WPF/GDI+的模板,若要使用须要本身配置。因此,通常状况下都是用C#。)
时代在改变 - 为何要选择 C#
在愈来愈多的场景当中,您均可以使用 C#来进行编程。咱们正在努力地作出一种改变。C# 在 Windows 当中是一种很重要的主要编程语言,但同时,咱们在其余平台上仍然还很是稚嫩。至少大多数平台是这样。如今 C# 已是全部平台上可选的编程语言之一,这很是鼓舞人心,然而咱们同时也有些顽固,此外这些平台上也出现了各式各样新颖的语言。这使得咱们迫切地但愿其余平台上也可以使用咱们的语言。
咱们已经不少次对咱们的语言进行了演进。实现 C# 底层的编译器和 IDE 技术(名为 Roslyn 项目)为 C# 的编程启用了独一无二的场景。其中一个好处是,咱们将 C# 的核心从 Windows 和 Visual Studio 当中剥离了出来,这意味着 C# 可以很容易地在其余 IDE 当中使用。您能够用本身喜好的 IDE 或者编辑器来编写 C# 代码。
咱们已经将 C# 从彻底的专有技术转变为了彻底开放源代码的技术了。这意味着每一个人都能给 C# 贡献代码了,固然也已经不少人参与到这个项目当中来了。咱们如今正在同社区展开交流,如今 C# 的演进很是迅速。由于如今这更像是一个协同项目了,而不是「微软说怎样就怎样了」。这很是让人兴奋。如今语言的变革已经再也不是三年才一代了。「这是咱们努力的成果,但愿您能喜欢它」,咱们如今天天都在与社区讨论将来的方向问题。咱们随时随地都可以在网上、Github 上获得反馈。所以,咱们语言设计的质量也愈来愈高。
让咱们从 C# 的各个项目开始一一介绍。
无处不 C# - Xamarin
Xamarin 之前是一家独立的公司。咱们六个月以前收购了他们。这是一种使用 C# 来构建跨平台应用的技术,用来制做原生的 Android 和 iOS 应用。它可让您使用相同的语言、相同的源代码来构建绝大多数应用组件,从而可以为多种不一样的移动平台编写应用。
它适用于 iOS 和 Android,一样也能够用在 Mac 之上,顺便提一下,Windows 也可使用。它能够建立高品质的原生 UI。有许多大型应用正在使用这门技术,由于它能够极大地减小单独在这些平台上编码的工做量。它也容许您使用与后台相同的语言,例如说 Java,不过 Swift 和 Objective-C 还未支持。它以支持的平台量取胜,是一门实现应用的绝佳语言。
它基于 Mono 项目,这是多年之前从微软离职的员工所实现的开源项目,而且一直维护,致力于可以在其余平台上也可以使用 C#。虽然在微软当中的咱们有些固步自封,可是他们却在咱们以前看到了 C# 跨平台的潜力,并实现了这个伟大的跨平台项目。Xamarin 正是基于此而生的,您在 iOS 和 Android 应用商店当中看到的许多应用都是基于 C# 的,要么是使用 Xamarin,要么是使用 Unity,这应该是业界领先的游戏引擎。
无处不 C# - Unity
Unity 也是一个基于 Mono 的项目,它的 2D、3D 游戏引擎是用 C# 来编写的。有不少游戏是用 C# 编写的。
无处不 C# - .NET 核心
在微软,咱们正努力完善 .NET 核心,这是对整个 .NET 技术栈、运行时以及代码库等内容的全新实现,旨在保证轻量、而且可供服务端使用,而且可用做云和服务器工做负载。它是跨平台的,适用于 Linux、Mac 和 Windows。咱们在此之上还放置了 ASP.NET 框架,这是一个被普遍使用的 Web 框架,目前您就能够在非 Windows 的机器上运行 ASP.NET 了,而且它还彻底开源了!为何咱们要创建一个单独的核心呢?这将有助于您可以构建更轻量的服务器。
首先,咱们移除了 UI 框架,可是 UI 框架可以独立部署。例如,您能够将运行时环境同代码一并发布;云端无需安装各式各样的依赖文件。它拥有一个更优秀的架构,更适合微服务的部署。它一样也致力于使咱们的服务端平台更加现代化。这些不一样的 .NET 运行在不一样的平台上,若是没有统一的部署,那么随着版本的扩散,一切就会变得很是的混乱,尤为是您做为第三方库提供方的时候。您须要某种东西可以跨平台运行,以便解决您的问题。
咱们一样也实现了一个名为 「.NET 标准库」的东西,咱们提供了一个全部 .NET 平台全兼容的 API。若是您须要使用它的话,您能够直接在工具库当中连接这个 .NET 标准库便可,它将能够在全部平台上运行。您也能够收回在 .NET 生态系统中随处可用代码的能力。咱们将随时对标准库进行升级,包括引入新的核心基本库,这样您任意连接所须要的标准库便可。所以 C# 可以在不少地方运行,但愿这可以说服你们来尝试 C#,由于这比之前有着更大的适用范围。可以实现这个着实让人兴奋。
Roslyn - C# 语言引擎
我想要多谈论一些底层的内容,也就是谈一谈 Roslyn 项目。
咱们对 C# 引擎进行了现代化。之前只有一个简单的 C# 编译器。它使用 C++ 编写的,以后咱们有了 Visual Studio,而后将 C# 集成了进去。然而这二者使用 C# 的方式都不一样,没有任何的代码共享,所以也没有办法知道对方的相关信息。若是有人想要使用 C# 相关内容的话,那就须要为之编写一套工具,或者为它们本身的 IDE 来添加支持,这是一个很大的工做量。由于人们不得不重头开始编写,由于 C# 引擎对它们而言是个黑盒。这对咱们来讲也并非让人满意的。
所以咱们决定,是时候重写编译器了,咱们不只重写了 C#,而且还采用了一些新的架构。用来实现 C# 语义的工具只能有一个。咱们须要构建一个你们均可以使用的工具,以便知足他们想经过 C# 实现某些功能的愿望。这个工具与平台无关,不管是什么样的平台,好比说批处理过程、编译器,仍是某些相似 IDE 的交互式环境,均可以使用这个工具进行。这是一个很难的目标,它花费了咱们大量的时间来实现,而且也投入了大量的人力。可是如今,咱们推出了 Roslyn API,它切实知足了咱们所设定的目标。
你们都须要知道,绝大多数 C# 工具已完成 Roslyn 引擎的迁移,不过仍然有一些没有。场下还有一位演讲者 Ted Brangs 的项目就是例外,由于他是出于某些技术缘由的考虑。咱们的想法是,这里是您须要用于实现 IDE 和编辑器的代码库。若是您须要使用不一样类型的分析工具,那么能够导入它们来对代码中的问题进行分析。若是您想要对代码进行操做的话,例如代码迁移、代码补全或者重构之类的操做,那么您也可使用它。若是您须要生成源代码,那么您也可使用它。若是您须要实现更多交互式场景,例如脚本或者 REPL(好比说如今咱们在 Visual Studio 当中包含了一个 C# REPL,它是在 Roslyn 的基础上构建的),那么这个引擎仍然可以编译代码并完成输出。
这里是您所可以实现的一个范例,也就是人们可以用 Roslyn 作些什么。这可能会致使编程语言相关的工具呈现爆炸式增加,由于人们如今能够更快地来对 C# 进行处理了。它可以与 C# 很好地协同工做。如今您已经有不少能够轻易获得的信息了,好比说语法、语义等内容,您就能够按照本身的想法,向所须要的地方添加特定的位码了。有一个社区项目充分利用了这点,那就是 OmniSharp。
(请记住:C#语言引擎--Roslyn)
OmniSharp - 随时随地可编辑 C#
OmniSharp 是一个特别的项目,旨在让 C# 可以在您喜好的编辑器上运行。他们实现这个功能的方法很聪明:因为 C# 如今已经能够在任何地方运行了,所以他们采用 Roslyn C# 引擎,而后在一个单独的进程中运行,无论您在进行开发的机器是什么(例如一台 Mac)。他们使用一个单独的进程来运行引擎,而后剩下所须要作的就是添加一个很简单的集成层,而后加入到特定编辑器中的集成层当中,这样双方即可以经过进程来进行通讯,从而让编辑器了解到 C# 的所有信息。
每次您按下了一个键,例如输入了点语法,而后这个时候您须要显示代码补全,这个时候它就会询问旁边的进程:「用户输入了点语法,那么我须要在代码补全列表当中显示什么呢?」。Roslyn 知晓全部的语义,它随后会告诉编辑器:「这五种方法是可用的,显示它们便可」。
经过这种分离方式,使得全部的语义分析过程被包裹在单独的进程当中进行,而后经过标准的数据传输方式来进行数据的交换。这使得咱们能够在不少的编辑器当中去实现表现良好的 C#,而且还能够提供语义感知功能(固然,这种作法褒贬不一)。
我须要再提一点,对于微软的 Visual Studio 而言,咱们使用 OmniSharp 来实现其 C# 模式,由于这是一个扩展,能够在任何地方加载。它不会内置在编辑器当中。C# 就像其余语言同样,对 Visual Studio 而言就是一个扩展组件。
(我的认为:OmniSharp项目对Roslyn引擎进行了封装,以更方便的使第三方编辑器调用,好比本身开发一个C#代码编辑器)
演示 - Roslyn 分析器
让咱们举一个更具体的例子。为了帮助人们将重点放在语言实现以外的地方,咱们建立了这个框架,名为 Analyzer,经过它能够轻松地对人们的源代码进行分析、诊断,最后输出结果。这样,咱们即可以提供代码修正建议了。
若是您须要:
一、您的组织有须要强制执行的代码格式
二、常常执行重构
三、想要与你们分享代码
四、须要让代码修正自动化
那么这个工具正是您所须要的。
这个项目您能够在 Visual Studio 当中安装,而后就能够开始使用了。当您打开某个项目的时候,它就已经自带了为这个项目而创建的样板代码。具体而言,当您的项目像这样进入调试模式的时候,而后分析器便会提取您的代码,而后进行代码修正,最后将结果输出。分析器能够以批处理代码的方式运行,也能够将其做为 Nuget 包进行分发。它是以 Visual Studio 扩展程序的身份出现的,在 Visual Studio 的完整版本当中,它是做为调试模式的一部分运行的。如今我运行了 Visual Studio,而后它便开始执行代码修正了。这个是我在完整版本当中的 Visual Studio 所编写的操做。
如今让咱们在这个完整版本的 Visual Studio 中打开一些代码。我尚未彻底实现完这些分析器的功能。这里是一些咱们想要进行操做的示例代码。出于简便起见,我想要实现的东西是语法分析,这里我能够定义各类各样的语义规则。Roslyn 引擎提供了完整的信息供我使用,我能够定义 if 或者 else 语句当中没有柯里化(curlies)语句是非法的代码样式。
咱们须要实现那种老式的、固化的代码风格,也就是始终须要添加柯里化的语句,由于接下来编辑代码的时候,并不会获得太多的 BUG。咱们须要在某些状况下阻止这种代码的出现,出于时间考虑,我这里只对 if 进行实现,咱们固然也能够将其应用到 else 规则当中来。这里让咱们来实现一个小型的代码分析器。
这里我不会中止使用这个完整版本。我须要放置一个断点。每当咱们看到 if 语句的时候,最开始须要作的就是要注册它,咱们须要调用这个方法 AnalyzeNode。每当 Visual Studio 中的源代码分析器命中了一个 if 语句,那么就会自动前往这里,而后我就能够执行一些操做了,随后它就会继续分析代码,直到命中下一个断点。如今我可以获得这段代码当中的所有信息了,接下来我就能够添加操做了。我获得的东西是一个 context 对象。
让咱们看一下这个 context 当中的内容。若是这个 if 语句不符合要求,我就能够报告一个诊断过程。我能够获得当前语法树的相关节点,一般就是这个 if 语句当中的内容。让咱们开始处理吧,咱们将鼠标悬停在这个上面,因为咱们位于调试模式,所以这里能够看到一个实际传入的对象。咱们能够看到这里的确就是一个 if 语句。让咱们使用 Roslyn 构建的对象模型,将其转换为 if 语句模型。
这里我可以获得一个语法树节点,它刚好是 IfStatementSyntax 的派生类。咱们能够声明 var ifStatement 赋值为该值。如今这就是咱们以后惟一所须要调用的对象了,我这里将再也不检查它是不是一个 if 语句。若是这个语句内部的东西不呈现柯里化的话,那么这个东西将被称为 Block,这就说明这段代码是不符合规范的。若是它不属于 SyntaxKind.Block 的话,接下来我就会提示错误了。我会告诉用户:「这里不对」。如今我须要汇报诊断结果。不过如今我尚未实现,我须要进行一点重构操做,来为之生成一个局部变量。
我能够经过 Diagnostic.Create 建立一个诊断,它须要我提供一些参数。首先是一个被称为 Rule 的描述符,而后我须要指定 location。也就是当问题出现的时候,我须要在代码当中显示波浪线。而后我须要指明当前违背了何种规则。而后指明不符合规则的位置。让咱们再对这段实现进行一下重构,生成一个局部变量。这就是我在调试模式所有所须要作的。
那么什么是所谓的「位置」呢?也就是我当前正在处理的这个节点:if 语句。那么咱们须要在那里放置提示信息呢?让咱们放在这个 if 关键字上吧。if 语句这里拥有一个 if 关键字,由于这是一个具体的语法树。它拥有里面代码的所有实现细节,包括全部的位置。让咱们从中取得相关的位置。这里经过 GetLocation 方法来获取。咱们获得 if 关键字的位置,而后将这个位置传入到这个方法当中。我编写了一些代码。让咱们移除这个断点,而后继续在调试器当中运行。咱们等待一下子,看看发生了什么,好的,如今您会看到 if 语句当中出现了波浪线。
这就是个人所有操做:编写三四行代码就能够识别出问题所在,并告诉框架在哪里显示这个问题。为了向您证实它可以起做用,我把这段代码注释掉,您会发现如今波浪线消失了。
当您试图实现更为复杂的操做的时候,就变得有点困难了,可是这仍然是一个相对简单的语言模型,由于它包含了完整的语法和绑定语义,人们能够用它来构建工具,而后与别人分享,这样便可以让每一个人在编辑 C# 的过程中遵循一致的原则,无论它们所用的编辑器是什么,只要是基于 Roslyn 的就行。不管人们位于哪一个平台,他们都可以从中收益匪浅。
我写的这个分析器一样能够在批处理模式当中运行。它能够成为编译过程中的一部分,能够标记警告或者错误,就如同编译器所作的那样。我还能够实现修复工具(但我以后的时间不打算演示这个功能),它能够基于咱们制定的规则对代码进行修复。
这就是 Roslyn 这边的演示,它是如何帮助人们得到更好的编码体验的,一个更好的 C# 开发体验。它给予了咱们更好的代码库,更好的架构,显然在 C# 当中,咱们能够对它进行 Dogfood 测试,以更好地帮助咱们演进语言自己。
C# 的演进
现在咱们对 C# 进行演进较之前已经容易不少了,而且咱们可让社区经过贡献代码而参与到 C# 的演进当中来。然而,这些演进的版本(我不会把全部版本都列述一遍)——展示了咱们在创新过程中的破坏性。
我想以 Async 为例。咱们编写了 LINQ,也就是咱们在 C# 版本 3 的时候引入的查询操做。咱们采起了一个颇有趣的冷门语言所存在的概念,并尝试将其主流化,将这些概念引入到 C# 这个主流语言当中,从而帮助它们开扩更广阔的市场。C# 当中的 LINQ 查询就是您可以在其余函数式编程语言当中找到的一个例子。
咱们把它们与 Lambda 表达式结合在一块儿。如今世界上绝大多数语言均可以使用 Lambda 表达式。固然几年之前这并不常见。现在 Java 也可使用 Lambda 了。固然,咱们仍是回到 C# 版本 2 来,在这个版本中咱们引入了泛型,仅仅只是慢了 Java 一拍,可是泛型已经成为了 C# 必不可少的一部分。由于在 C# 当中,泛型是深嵌入运行时当中的。Java 则是采起了比较谨慎的方法,让泛型直接编译成所须要的东西。
当您将泛型机制实如今运行时当中的时候,这对获取语义而言是 100% 有好处的,然而这一样也意味着性能特性将大相径庭,尤为是当语言中存在值类型的时候,当 C# 从版本 1 升级的时候就遇到这个问题,Java 可能会在将来采起这种方式来实现泛型。当语言当中存在值类型的时候,您但愿泛型可以识别出它们,而且专门为这些值类型设定特定的规则,也就是在使用泛型的时候就无需对其进行封装和分配内容空间。这样泛型就可以让代码更快,而不是产生拖累。
自咱们升级以后,泛型便成为了不少语言的标配特性。因为泛型的引进,咱们借此便可以正确地实现查询功能。因为泛型是深嵌入在运行时当中实现的,所以咱们可使用反射机制,这样咱们即可以实现一些奇怪的代码引用,而后将 C# 代码转换为 SQL。这都是基于类型能够变换的机理才得以实现,而且甚至在运行时都是能够如此使用。
咱们将动态值集成到了静态类型的系统当中,这种类型的类型是动态的、变化的,咱们称之为 Dynamic。再次强调,其底层实现仍然是基于泛型来实现的,这使得其运行效率高效,而且避免了不少无谓的封装操做。Async 则是深度依赖于泛型机制。在 C# 版本 6 当中,咱们引入了 Roslyn,所以对于实现任何特定的语言特性而言,再也不是一件难事。咱们如今拥有了更多的灵活性,如今咱们即可以实现那些还未实现的微小特性了,咱们经过实现这些特性,可使得开发工做更简单、更优雅、更简洁、更轻松。
以后咱们在 C# 版本 6 当中引入了 swath ,也就是如今的 C# 版本。而后在以后的 C# 版本 7 当中,咱们将再次引入一些更重要、更底层的特性,咱们想要从函数式编程语言当中进行大量借用特性,我认为咱们下一步多是须要引入相关的特性,来处理那些没必要用面向对象的方式处理的数据。你们都知道咱们一开始是一门很是纯粹的面向对象语言,而后只是倾向于添加一些函数式的特性来做为面向对象的一些扩充,而且咱们也在尝试将这二者很好地结合起来。这个灵感来自于 Scala,而且 JVM 也在这么作,尝试将函数式和面向对象结合起来,可是咱们绝对不会抛弃面向对象这个根本。
(Scala是一门多范式的编程语言,一种相似java的编程语言,设计初衷是实现可伸缩的语言,并集成面向对象编程和函数式编程的各类特性。)
演示:Async
我打算跳过这个 Async 的演示,由于大多数人可能都知道它的做用是什么了。那么让咱们来谈一谈即将到来的 C# 版本 7。
(C# 1.0 with Visual Studio.NET
C# 2.0 with Visual Studio 2005
C# 3.0 with Visual Studio 2008,2010(.net 2.0, 3.0, 3.5)
C# 4.0 with Visual Studio 2010(.net 4)
C# 5.0 with Visual Studio 2012(.net 4.5),2013(.net 4.5.1, 4.5.2)
C# 6.0 with Visual Studio 2015(.net 4.6, 4.6.1, 4.6.2)
C# 7.0 with Visual Studio 2017(.net 4.6.2, 4.7)
C#版本是VS版本决定的,也可与相应的.net framework版本对应起来)
C# 版本 7
让咱们从元组 (tuple) 开始。首先这里我有一个完整的程序。而后里面有一些数字值。由于数字是很是容易理解的,可是可能不是全部人都可以知晓其中的含义:咱们如今拥有的是二进制的字面量。这是一个很微小的特性。当您教孩子编程的时候,这就很是有用了,这些位于数字下方的则是位 (bit)。我如今要再添加一个。咱们这里一样还有数字分隔符,就像其余语言所作的那样,您能够将这些分隔符放在须要的地方,从而让数字更容易阅读。
我这里打算实现一个名为 Tally(计数器) 的方法,它将数组当中的数字累加起来,得出计算结果。这样咱们即可以对这些数字调用这个 Tally 方法。固然目前这个方法我尚未实现,让咱们使用重构来生成这个方法。这是一个静态方法。目前它的返回值为 Void。或许它应该返回其余的东西。我以为它应该要返回这个累加值,或者直接返回数字的总数?我以为二者都是很是重要的。可是如今在 C# 当中您只能返回同样东西,可是在不久的未来,您就能够返回两个返回值了,甚至三个、四个、更多。只要您想,您实际上能够建立一个超大的元组,可是这多是个糟糕的主意。
如今让咱们返回两个 int 值。这是一个元组类型。它表示里面有两个 int 类型,这应该很容易理解。这里是元组字面量,咱们仍是先返回一些虚拟的值。这里面包含了一些所需的值,经过括号和逗号包裹起来,固然这个语法应该比较正常。当我使用这个方法的时候,我能够获取它的返回值,而后我会发现我获得了一个元组类型。
那么咱们该如何使用元组呢?让咱们将其输出出来。插入字符串。总和可能位于这里的第一个位置。让咱们来看一下元组有些什么:Item1 和 Item2。很显然,咱们知道它们分别表明了什么,咱们能够直接使用这两个名称。虽然这个名字比较糟糕,可是能用就行。C# 当中的元组还能够为不一样的元素赋予不一样的名称。这里我将指定各个元素的名称。这是什么意思呢?
当我获取到这个元组的时候,它即可以告诉我它里面的元素是什么。这一样也意味着我来到这里,输入点语法,就能够看到这些预览而已;最终的版本应该须要隐藏掉这些糟糕的名字。这里咱们会看到拥有了很显然的名字,由于这里您能够看到 sum,咱们能够直接使用它来获取元组当中对应的值。以前那些是底层当中的真实名称,可是编译器知道跟踪这些别名,并使用它们来替代显示。
元组当中拥有元素别名是很是重要的,由于您极可能记不住这个元组是姓在前仍是名在前。所以元组须要提供这些信息,这样才可以让人容易理解。您须要有获取别名的能力。
固然,您极可能会但愿在获取到元组的时候,就马上将其解构,将元组当中的值分开,固然您也能够在 C# 当中作到这一点。您能够在这里声明 sum, count,而后元组便会马上解构为 sum 变量和 count 变量。这样咱们即可以再也不使用 t. 前缀,而是直接使用 sum 和 count。
让咱们如今来实现这个方法。这里咱们再也不返回一个虚拟值,让咱们返回一个真实的结果。这里咱们对这些数字执行 foreach 操做;这里咱们将其称为 values。接下来咱们在每次遍历的时候更新结果值。
result 这个名字太长了,我想将其命名为 r。让咱们声明 r = 这个新的元组字面量。这里我但愿可以获取旧有的值。我但愿 r 一样也有元素别名。让咱们前去给其增长别名。您能够在元组字面量当中为其赋别名。这里的语法和命名参数所作的相同。如今 r 拥有了 s 和 c 两个元素别名。咱们能够调用 r.s,即可以在这里获取到以前的总和值,而后加上新的值 v,而后这里的 r.c 总数须要加 1。
您可能会在想,这不是很复杂么,很浪费空间。这不是每次都直接分配一个新的数组?或者说在每次遍历的时候偶会建立一个新的元组么?这样作的话,在资源受限的设备或者须要花钱的云端当中是否是很很差?为这些元组分配内存空间是否是很是浪费?
这并不会致使空间的浪费,由于元组不属于对象。元组被实现为值类型,是以 C# 当中的结构体实现的。所以它们不会分配内存空间。它们只是直接更新某些在堆上的东西。这些值类型是使用 copy 来传递的,而不是经过引用来传递。元组没有标识,它们当中仅仅只是包含值而已(我以为元组应该是这样的)。它们应该只是短暂的存在。所以它们不该该拥有生命周期,这样才能更有效率。
元组不只是值类型,它们一样也是可变类型。我知道在函数式阵营当中的人们会很反对这种作法,可是我仍是坚持元组是可变类型。您能够修改元组里面的值。而不是这样子写:r.s += value。做为一个单独的语句 r.c++ 就很好了,就不用更多的重复了。此外我还能够交换元组当中元素的位置,这并非危险的操做,由于在线程之间没有共享的可变状态,由于这是一个结构体。没有对象去共享它。您能够随意将其传递到任何地方,它是用拷贝操做执行的,不存在危险的状况。
为何咱们总要强调面向对象呢?为何一切都必需要封装起来呢?元组没有属性,仅仅只是字段。它们是包含某些可变公共字段的结构体。获取很是简单,您能够很轻松地明白本身的作法。这就是元组的正确用法,由于它们不是抽象类型;它们不会封装任何东西——它们仅仅只是值而已。
关于元组的其余几件事是:因为元组是一种类型,所以它能够判断相等。例如,您能够将元组做为字典当中的键来使用。若是您想要让两个类型都做为某个字典的键,那么使用元组是再好不过的,这样一切都相安无事。哈希值和其余所用的东西在数据结构当中都可以正常的工做。
固然,这也是从异步方法当中获取多个值的绝佳方法,由于若是操做的是异步的话,您能够返回 Task 的元组,这样当全部的操做结束以前,您就能够依次等待。获得元组以后,就能够对其解构并继续前进。元组是很好的传输工具。对于 async 方法以及其余方法而言,若是有多个返回值的话是很是糟糕的,由于您没办法输出多个参数,可是如今经过元组您能够实现这个操做了!
(若是须要使用同一类型的多个对象,可使用集合和数组;若是须要使用不一样类型的多个对象,可使用元组(Tuple)类型。.NET Framework定义了8个泛型Tuple类和一个静态Tuple类,它们用做元组的工厂。元组用静态Tuple类的静态Create()方法建立。Create()方法的泛型参数定义了要实例化的元组类型。)
将来展望:更多的模式
咱们开始向 C# 当中添加模式匹配 (pattern matching)。
1 if (o is Point(5, var y)) { WriteLine($"Y: {y}"); } // 递归模式
2
3 state = match (state, request) // 匹配表达式,匹配元组
4 { 5 (Closed, Open) => Opened, 6 (Closed, Lock) => Locked, 7 (Opened, Close) => Closed, 8 (Locked, Unlock) => Closed, 9 };
这里咱们从函数式阵营当中引入了一个全新的理念,咱们正在逐步实现这个功能。您会在将来的 C# 版本当中见到更多的内容,可是如今让咱们跳过这里,介绍一下第一种模式。
让咱们把这个例子变成包含递归数字列表的状况。这里咱们用的不是 int 数组,而是一个对象数组,其中咱们有一个约定,其内部的东西是 int 值或者是其余包含 int 的数组,也能够是新的对象数组,其中有一些 int 值嵌套在当中。或许若是里面也能够包含 null 可能会让人能更加明白,如今咱们须要更新一下咱们的这个 Tally 方法,让其可以处理这个数组。
首先让咱们直接替换为这个对象数组,好的如今咱们获得了一个错误,由于这个 v 再也不是 int 类型了;他是一个对象。咱们须要知道它是不是 int 值,若是是咱们就添加它。所以咱们须要进行一些逻辑处理;在过去,咱们会执行一个类型检测。若是 v 是 int 类型的话,而后咱们就执行转换并处理;可是,即使咱们检测出它是 int 类型,这里咱们实际上仍然不知道它是什么。咱们必须再次执行检查才能将其放入。
相反在这里,您能够将其认为是对 is 表达式的一个扩展。您如今能够声明一个变量。当您询问它是否 is int 的时候,若是是,那么就会取这个 int 值并将其赋到这个新的变量 i 当中。接下来变量 i 就有 v 的值了,可是类型已经肯定为 int 了。如今咱们就能够在这里将其添加进去,运转良好。
is 表达式如今扩展为容许模式的存在了,这是 C# 当中引入的一个新概念。模式,而不是类型。模式能够是不少复杂的组合。如今模式还不能很复杂。基本上只可以容许常量或者类型模式。例如,能够设定常量值 v is 7(如今这个被容许了,由于这属于常量模式)。咱们正在实现更多的模式,将它们集成到语言特性当中来,好比说表达式。
另外一个咱们正在集成的地方是,咱们正在尝试将其整合到 switch 语句当中。我如今能够对任何东西进行 switch,而原来 swtich 只能够对原子类型进行操做。这是很古老的特性了,不过如今它能够对任何东西进行操做了。咱们能够对对象进行操做:switch on v。在个人这个 switch 语句当中,我能够列举没有任何常量存在的状况,如今这个属于一种特殊的模式,不过能够对任何模式进行操做。我能够这么声明 case int i。(我必须记得要 break,这就是为何在这里我获得了一个波浪线)。
我这里已经用了一种模式。我扩展了 switch 语句当中了 case 块,以便其可以应用某种模式,而且能够「当这种模式适用时,就执行此 case 块」。我能够对 swtich 语句进行现代化。我可让对象数组成为 case 的条件,这也是我所期待的另外一件事。让咱们将其声明为 a,我能够将条件放到 case 里面。我能够设定「我只须要长度大于 0 的对象 a,由于 a.Length 大于 0(不然就没必要执行其余操做了)」。在这种状况下,我能够设定 var t = Tally,而后加入嵌套数组,并将结果添加到 r;r = r。您知道后面的用法:r.s + t.sum、r.c + t.c。而后 break 退出。这是对既有模式特征的一种泛化,也就是 C# 当中模式匹配所拥有的程度。
在将来,咱们但愿可以加入更多的模式。咱们须要更智能的模式。您应该须要可以使用递归模式。咱们让您可以指定一个给定的类型,让其可以被解构。例如,您能够在 Point 类型进行指定,这样它就能够被解构,就像咱们以前对元组进行解构,分解为不一样的变量里面。当类型被设定为可解构的以后,咱们就将其与模式匹配结合在一块儿,并容许您可以:同时检查 o 是不是 Point 类型,若是是的话就对其进行解构,而且能够应用递归模式:若是 o 是一个 Point,那么这个点的第一个部分 x is 5,而后将第二个部分放到变量 y 当中。您能够获得更智能的模式。您也可使用它来构造不可读的代码,可是通常而言,若是可以更深刻模式,那么您就会发现模式是很是有用的。
咱们应该须要在新的地方当中添加模式。switch 语句是 20 世纪 60 年代的产物了。或许咱们能够新增一个 switch 语句的表达式版本。或许是一个匹配表达式,而这是函数式语言当中所称呼的,它具备更新的语法,基于表达式,而后 case 语句中也能够添加表达式,这样可让代码更为简洁。可是如今咱们已经有了模式的概念,咱们能够添加新的模式,而后向新的地方添加新的模式。这就是咱们下一个版本的 C# 所须要关注的一件事,咱们已经在努力实现它们,由于 C# 版本 7 已经差很少完成了。(咱们尚未发布,也不要问我何时发布)。
(此部分关于C#新版本模式的概念理解起来比较模糊,仍是待之后版本发布后实际使用一下体现会更贴切。)
将来展望:可空的引用类型
1 string? n; // 可空的引用类型
2 string s; // 不可空的引用类型
3 n = null; // 容许;它是可空的
4 s = null; // 警告!不该该为空
5 s = n; // 警告!类型不一样
6 WriteLine(s.Length); // 容许;是不可空的
7 WriteLine(n.Length); // 警告!它可能为空
8 if (n != null) { WriteLine(n.Length); } // 容许;您进行了类型检查
9 WriteLine(n!.Length); // 若是存在的话固然能够
新的语言当中,有一个特性正在成为主流,那就是类型系统可以进行区分的能力,也就是判断类型是否可空。
变量有时候可能会为 null,由于它是值域的一部分;可是有些时候我并不但愿空值出现,那么为何我须要随时对引用错误进行处理呢?Swift 也有这项功能。咱们可否让 C# 也实现这些功能呢,即使咱们如今已经推出了 7 个版本,而可空性彻底是一个基于运行时的玩意儿呢?
咱们认为能够:咱们已经在 C# 当中为可空值类型留下了尾随问号标志。若是咱们容许您将它应用于引用类型,或许这就是您陈述某个类型为空的方式。另外一方面,若是您不这么声明的话,就说明您指望那里的东西不可能为空。
咱们将帮助您进行维护,这意味着我能够将 null 分配给 n,可是不可以分配给 s,而且若是没有任何限定条件的话我也没法将 n 赋值给 s,由于 n 的值极可能是 null。我保护变量以防止它持有不应持有的值。另外一方面,当我想要使用这个引用的时候,我能够无需任何限定就执行 s.Length,由于咱们知道它可能不会为 null。咱们没法让像 C# 之类的语言作一个保证,保证这里必定有值。
n.Length 会警告您它的值多是 null,您可能会获得 null 引用异常。解决的方法是,这些新语言当中存有一种新的空值检查特性。它们有一种新的模式匹配方法(或者某种能够用来检测 null 的东西)。咱们不打算改变您检查 null 的方式。在 C# 当中,已经有 7 种检查空值的方式存在了。相反,咱们想让编译器对其进行跟着,来检查某个类型是否为空。
若是您有一个 if 语句,来判断 n 不为空的时候才进入到其中,那么咱们就知道这个范围已经通过了类型检查了。事实上它的值不可能为 null,咱们将会假设您使用点语法访问内部成员是没有任何问题的。那么有没有别的办法处理它呢?固然有,然而您如今仍然还须要使用这种方式。您必需要随时使用这种方法才能消除全部的空值引用异常。
此外还有一个「强制」操做符,您能够给某个可空值上面加一个前置感叹号 (!),这就意味着您强制让其不能为空。您知道在这里,它的值永远不可能为空,只要你能足够勇敢、足够确定,那么您就可使用点语法,也就没有警告的产生。咱们已经在开发这个功能,咱们但愿可以在下一代 C# 当中获得这个功能。
但愿这个功能是很是有用的。关于这个特性的一件趣事是:它不只须要深刻到语言内部进行调整,还须要确保咱们全部的框架都应用上这个特性,这样您才可以确保本身的代码应用上了正确的可空值。这是一个很是具备挑战性的功能,我以为这是很是值得的。
(我的看法:微软在下一个C#版本中增长此定义, 目的仍是为了代码的安全性。如今的引用类型在定义时并无如此分开定义声明,之后在定义如string s这种定义时,可潜移默化的表示是不能够为null的,这样一方面能够不用进行if(s==null)这样的判断;另外一方面也同时保证了在忘记进行此类判断时,程序也不会“抛出未将对象引用到定义”等此类的异常。反之,若想定义能够为null的引用类型,则能够以string? s的形式示人,算是微软在语法方面更加规范化了。)
备注:本人基于对原文的理解,增长了我的备注(紫色斜体括号部分),如有错误,请读者提出意见和见解,愿和你们一块儿进步!
原文连接 by Mads Torgersen on Dec 27 2016