当前位置: 首页 > news >正文

.NET成年了,然后呢?

.NET成年了,然后呢?

作者|Lex Li

编辑|郭蕾

这可能是唯一一篇系统回顾 .NET 发展的文章。.NET 的成年礼到了,你会送它什么?

2014 年 11 月 12 日,美国纽约曼哈顿,多云,气温适宜。微软公司执行副总裁“红衣主教” Scott Guthrie 换好他标志性的红色 Polo 衫,为即将召开的 Connect 大会主题演讲做着最后的准备。他从 1997 年毕业加入微软起便参与 .NET Framework 的研发行动,是 ASP.NET 技术的奠基人。此后 Scott 逐渐转入管理者的角色,从领导 Web 相关技术研发团队起步,到掌舵整个开发工具部门,再到近年来执掌 Azure 云计算平台相关的庞大事业部。一路走来,他已经经历了不少类似的会议,像早年的 Professional Developer Conference,后来的 MIX 和最近几年的 Microsoft Build。不过 2014 年初,Steve Ballmer 将微软公司的帅印交到 Satya Nadella 手中,一个全新的公司形象呼之欲出。一系列刚刚得到批准的新工程,即将在本次 Connect 技术大会上对外公布。

音乐响起,Scott 健步如飞走上舞台,开始阐述过去几个月来微软公司在 Mobile First Cloud First 战略下取得的一系列成就。Azure 云计算平台迅速发展壮大,使微软得以紧追 AWS 这个领头羊,与其他虎视眈眈的竞争对手拉开身位。另一件值得高兴的事情,是微软与主打跨平台移动开发的 Xamarin 公司将进一步加深合作,共同推动 .NET 技术在移动应用领域的发展,以弥补 Windows Phone 平台移动市场占有率不高的短板。

当“创新”、“敏捷”和“开放”这几个关键字眼出现在大屏幕上,敏锐的观众必然会联想到微软公司的变化。是的,在过去很短时间内微软开始在 Azure 中紧密集成多种开源软件和 Linux 操作系统,甚至打出了“微软爱 Linux”的宣传口号。不仅如此,前几年它又实验性地公开了一批项目的源代码,如 ASP.NET MVC 和 Roslyn。那么下个阶段微软打算怎么在开放性方面更进一步呢?Scott Guthrie 兴奋地宣布微软即将发布一个名为 .NET Core 的全新开发平台,这个平台的全部代码将基于开源协议完全公开。话音未落,会场内立即响起一片掌声。好消息还不仅仅这条,Scott 又介绍到,不同于前代 .NET Framework,这个 .NET Core 平台除了支持自家的 Windows,还将支持主流的 Linux 和 OS X(后来改名为 macOS)等操作系统。会场内再次响起掌声。

商业公司开源自己的主打产品,从九十年代起已经不少,到后来虽然不算大新闻,却每每传为佳话。例如网景公司从 1998 年 1 月起开源自己的浏览器即 Mozilla 项目,到 2014 年已经过去十六年。2006 年起 Sun 公司逐步开源了 Java 的绝大部分源代码给 OpenJDK 项目,算起来也已经过去了八年。作为一家和开源社区曾经矛盾重重的商业软件公司,转型中的微软在这个时候又迈出了关键的一步。

生于忧患

九十年代初,正是微软公司通过 Windows 操作系统和 Office 办公软件在桌面市场攻城略地战无不胜的时代。两个 Windows 平台开发者常用的工具也来自微软:Visual Basic 是一个快速开发工具,只需拖拽控件配合简单代码就能做出一个不错的桌面软件;Visual C++,语言复杂,框架复杂,好在功能全面,能够覆盖从系统驱动到复杂应用程序等各种开发场景。因此即使 Delphi 和 PowerBuilder 等第三方工具虎视眈眈,微软自主开发工具的市场占有率还是稳如泰山。

浏览器时代的到来和客户端服务器模式的普及,很快改变了软件开发的格局。1995 年 Java 和 JavaScript 两门新兴语言的出现,让更多开发者开始将目光投向了新的领域。在浏览器方面,微软很快推出了 Internet Explorer、JScript 和 VBScript 去应对挑战。在服务器方面,微软也开发出 IIS 和 ASP 来填补空白。最令人意外的是,微软向 SUN 公司购买了 Java 的授权,进而推出 Windows 平台的 JDK 和开发工具 Visual J++。这一系列的动作使微软得以紧跟竞争对手的步伐。但仔细分析之下它们既没有形成一个统一的编程模型,也没有和微软已有的技术 Win32/COM 进行很好的整合。Visual J++ 的 6.0 版本曾经一度让人看到全面整合的希望,但是微软和 SUN 公司之间的法律纠纷让它止步不前。对于野心勃勃想在桌面软件领域保持霸主地位同时进入新市场的微软来说,此刻它急需掌握一个新的开发平台,来迎接新千年的挑战。

几年的低调研发之后,微软终于在 2000 年公开 .NET 技术,让广大 Windows 开发者看到了新气象:

  • 统一的程序运行时(.NET CLR)

  • 丰富的开发语言选择(C#、Visual Basic、C++ 和 J#)

  • 统一的公共函数库(Base Class Libraries)

  • 便利的开发框架(Windows Forms 和 ASP.NET WebForms)

  • 便利的开发工具(Visual Studio .NET)

这些技术在 2002 年初正式发布,迅速取代了之前老旧的 VB 6 和 ASP 等技术,也推动了 Windows 平台的软件升级换代。那些曾经给微软造成压力的第三方技术,如 Delphi、PowerBuilder 也慢慢淡出了大家的视野。

从 2003 年起,微软按照一个略显缓慢但是还算紧凑的节奏开始给 .NET 构建一个完整的生态系统:

  • 2003 年,Windows Server 2003 发布,搭载 .NET Framework 1.1。微软同时发布了 Visual Studio 2003。这个小版本升级填补了很多 1.0 时代的空白,甚至带来了 Compact Framework 这样针对 Windows CE 移动设备的新技术。

  • 2005 年,.NET Framework 2.0 和 Visual Studio 2005 发布,加入虚拟机级别的泛型和 64 位程序开发的支持。与 SQL Server 2005 的深度整合,使得程序开发更有效率。微软逐渐补全了 .NET 工具链上的短板,如此时推出的 MSBuild 构建工具和 MSTest 单元测试框架。

  • 2006 年,发布 .NET Framework 3.0,并引入了 WPF、WCF 和 WF 等新框架。开发复杂软件系统,不论在界面设计还是通信层面都变得更为简单和标准化。

  • 2007 年,通过 .NET Framework 3.5 和 Visual Studio 2008 微软又为开发者带来了 LINQ 和 AJAX 支持。前者使得 C# 和 VB 语言能在很大程度上取代 SQL,提升了数据查询方面的开发体验。后者使得 ASP.NET 网站应用开发跟上了业界潮流。后续发布的 .NET Framework 3.5.1 进一步加入 Entity Framework 这个实体数据映射引擎。同年微软还发布了 Silverlight,开始尝试利用 .NET 技术来进行浏览器端应用程序的开发。

  • 2010 年之后,微软又推出了.NET Framework 4.0 和 4.5 等后续版本,在并行计算方面推出了 Task Parallel Library 新框架和 async/await 新语法,不仅大大降低了 .NET 平台多线程软件开发的复杂度,更是对其他开发语言的设计产生了深刻影响。比如 JavaScript 等其他开发语言就借鉴了 async/await。

2006 年起 Java 成为开源项目,伴随着 Android 手机平台和 Hadoop/Spark 大数据平台再现活力。而这个时候的 .NET 技术,已失去它当初刚发布时的夺目光彩。虽然 Windows 桌面和服务器端的开发者会为这些稳步更新感到激动,但放眼外面的世界,Ruby on Rails、HTML 5、大数据、移动开发,新技术的潮流一波又一波,都没有看到微软 .NET 的身影。2014 年 .NET Core 的开源,给人留下了遐想空间。

筚路蓝缕

从网景公司决定开源浏览器代码到 Mozilla 项目正式上线,整个过程历时三个多月。SUN 开源 Java 的过程更长一些,先是花了大半年的气力将绝大部分代码公开,然后 OpenJDK 项目方面慢慢接收。而这都还仅仅是万里长征第一步。围绕这些代码必须打造一个活跃的开源社区,才能保证项目未来的持续发展。所以微软宣布要打造一个全新的开源平台,那么就注定需要一个漫长的研发过程。

开源项目 Mono 在这个时候伸出了橄榄枝。Mono 最初定位为 Linux 和 Mac 桌面应用开发平台。2008 年起,它与 Unity 游戏引擎的合作惠及数百万游戏开发者,同时还通过 Xamarin 这个品牌建立了一套跨平台移动开发工具,客户包括 Honeywell、国家仪器、西门子等一万五千多家公司。溯本追源,竟是 2000 年微软联合惠普与英特尔提交给 ECMA 的 .NET 平台相关技术标准。Mono 项目多年积累的各种经验,对于微软工程团队来说颇有借鉴价值,算是慈乌反哺了。

微软更是马不停蹄地开始执行以下这些工作:

  • .NET Framework 的代码之前的授权协议有诸多限制,仅仅能够用于代码调试等有限场合。经过重新授权后,这些代码以开源协议发布,使得 Mono 项目可以加以集成,改进 Mono 和 .NET 的兼容性。

  • 在 GitHub 上创建 CoreFX 和 CoreCLR 等代码仓库,分期分批地将 .NET 的核心代码发布出来。利用 Mono 环境来进行跨平台的代码构建,同时建立公开的持续集成服务器,使得 .NET Core 平台的构建过程完全公开透明。(当然随着 .NET Core 工具链的成熟,它的构建过程后续不再需要 Mono 的帮助。)

  • 建立独立运营的 .NET 基金会,接纳乐意推动 .NET 技术发展的合作伙伴,同时全局管理 .NET 相关的核心资产和项目。

  • 在 GitHub 等平台上展开公开讨论,凝聚共识来建立社区。这样,一方面规范了微软代码的发布过程,一方面也制定了补丁审核等基本流程,使得 .NET 相关的核心项目可以接受来自社区开发者的代码。

在 .NET Core 的 GitHub 仓库中,另有一些有趣的讨论。从中我们可以窥见这个开源进程向前推进的一些动力之源。

一个例子是上面提到的持续集成服务。微软团队使用一个比较复杂的私有系统来构建 .NET Framework。这种方式当然不适合 .NET Core 这个开源项目的需要。因此 Mono 团队的 Alexander Köplinger 在 GitHub 上建议使用 Travis CI 或者 AppVeyor 服务来做搭建一个公开的持续集成平台,并且提供了一个可用的 AppVeyor 配置脚本。微软欣然接受了这个建议。当然如今基于开源的 Jenkins 工具微软新做了一个更为公开透明的方案。

另一个例子是,当时 Protobuild 工具被 MonoGame 项目用来生成跨平台的工程文件,显示出很大的潜力,所以它的负责人 June Rhodes 向微软建议采用 Protobuild 来处理 .NET Core 源代码中的工程文件。微软团队详细阐明了他们对于工程文件格式的想法,清楚解释为何 Protobuild 并不适合 .NET Core 的需要。

当然,还有一些做静态代码分析工具的公司或是比较资深的技术顾问,在 GitHub 上发帖来指出微软代码中的不足之处和可以采用的修补方案,巧妙地做起了小广告。微软团队每次也是及时回应,或是很快地修复了潜在问题,或是指出没有采纳相关建议的原因。

其他非常有价值的外部支援,比如来自业界其他大公司的补丁,也都是以 GitHub pull request 的形式出现,方便审查和存档。例如 2015 年 5 月,英特尔的工程师向 .NET Core 捐赠了一批有关 CRC 算法的补丁,使得 GZipStream 等常用类型性能倍增。2016 年 6 月加入 .NET 基金会的三星公司更是不遗余力,不仅令 .NET Core 成为自家 Tizen 计算平台的应用开发标准配置,也使 .NET Core 程序能够支持其他采用 ARM 处理器的类似平台(如树莓派)。

在 Connect 2016 大会上,微软终于详细公布了 .NET Core 平台开源两年以来的一系列数据,例如新近提交给 .NET Core 相关项目的贡献超过 60% 都来自于社区。这表明,虽然 .NET Core 暂时还比较依赖微软,但它背后已经有一个活跃的社区,而且微软和这个社区保持着良好的互动。

蜀道艰难

当然,一旦选择代码开源,并且将设计和发布的流程逐步从公司内部转移到 GitHub 这样的公开平台,微软同样需要去直面这些转变带来的种种挑战以及负面影响。

第一个挑战,是如何把控预览版本、发布候选版本和正式版本的节奏。从 2015 年年初起,微软就连续发布了多个 .NET Core 平台的预览版本,不过到 11 月项目开源满一周年的时候,开发计划中的任务还没有全部完成。11 月 18 日在微软的官方网站上却出现了首个发布候选版本。事后来看,这个版本存在不少尚不完善的地方,用户体验不佳,其实继续标记为预览版更好。微软之后也立即针对性的调整了开发计划。等到 2016 年 5 月 16 日发布 .NET Core 第二个发布候选版本时,产品质量显著提升。

第二个挑战,是选择一个合适的版本号。在 2014 年底这个平台刚公布的时候,名字还是 .NET Core 5 和 ASP.NET 5。微软最初选择 5 作为版本号,可能是希望它成为 .NET Framework 4.x 的延续。但这个版本号很容易让部分用户认为这个新平台是 .NET Framework 的简单升级。然而 .NET Core 从本质上说,是一个完全独立的全新平台,和过去的 .NET Framework 有着天壤之别。因此微软收集各方意见之后,在第二个发布候选版本时开始启用新命名,.NET Core 1.0 和 ASP.NET Core 1.0。

第三个挑战,是如何为 .NET Core 这个新平台搭配合适的工具链。这也是一个非常重大的抉择。前面提到,.NET Core 诞生之初没有一个完整的工具链,连最初的构建过程都需要 Mono 来配合。原本微软的计划是复用当时为 UWP 全新开发的 project.json 工程格式,并围绕这个格式设计出以 DNX 为核心工具的一套工具链。在第一个发布候选版本中,微软也确实搭载了 DNX 等命令行工具。但是棘手的问题出现了,从 2005 年 .NET Framework 2.0 时代开始,整个 .NET 生态环境是围绕着 MSBuild 建立起来的。DNX 和 MSBuild 并不兼容,因此微软必须决定是否完全抛弃 MSBuild 而围绕 DNX 重建一个生态系统。几经权衡微软放弃了 DNX,回到了 MSBuild 之上,开源了相关代码并围绕它重新设计 .NET Core SDK。这也是为什么到了第二个发布候选版本面世的时候 DNX 完全消失,而 MSBuild 以及新的 dotnet 命令行工具出现了。

从上述内容可以看出,其实到第二个发布候选版本止,围绕着 .NET Core 这个平台的各种摸索才算是尘埃落定。在 Stack Overflow 等问答站点上,短短几个月内询问 DNX 和 dotnet 两套工具链如何迁移、有何异同的问题一下子多了起来,甚至 DNX 被废弃很长时间之后仍然零星出现。这反映了微软当时在设计上面的反复确实给开发者们带来了不小困扰。

当然,解决这些问题最好的方式,就是发布一个稳定可靠的正式版本。微软选择了 6 月 21 日 Red Hat 公司主办的 DevNation 技术大会,在一个稍显奇怪的场合发布了 .NET Core 1.0 和 ASP.NET Core 1.0。

说场合奇怪,一方面当然是因为会议本身不是微软自己主办。另一方面是因为发布会当时台上的几个技术演示内容都有点不同寻常。它们分别是:

  • Red Hat 公司演示他们为微软刚刚开源的编辑器 Visual Studio Code 设计的 Java 语言插件。

  • 微软公司演示可以运行在 Red Hat Linux 上面的 .NET Core 1.0。

  • Eclipse Che 的负责人演示 Che 这个新 IDE 对于 .NET Core 和 C# 的支持。

怎么样,是不是觉得台上这群人都拿错了剧本?如此有趣的方式,反而契合了当前 IT 技术发展的潮流。现在已经没有哪家公司能够仅靠种好自己一亩三分地就旱涝保收。用户系统的多样性使得每个品牌都必须更好的了解别家技术,加强互操作性,有时候甚至要“越俎代庖”,通过合作构建一个更加开放的整体大环境。

.NET Core 1.0 运行环境是正式发布了,但与它搭配的工具链 .NET Core SDK 仅仅是一个预览版本。出现这种状况,很大程度是由于 MSBuild 开源较晚,很多从 DNX 工具链迁移到 MSBuild 的具体开发工作还没能结束。此后 .NET Core SDK 的研发虽然紧锣密鼓,但又错过了 2016 年 11 月 16 号的 .NET Core 1.1 发布会。直到 2017 年 3 月 7 日 Visual Studio 2017 正式发行,.NET Core SDK 1.0 才姗姗来迟。

为何以上要讨论这么多微软干得并不漂亮的地方呢?项目如此反复,难道不是在打微软的脸吗?只需我们静下心来看看时间表,就会发现——.NET Core 从 2014 年 11 月项目正式公布,到 2016 年 6 月运行环境 1.0 正式发布,其实才不过一年半时间,而到 2017 年 3 月 SDK 1.0 正式发布,整个.NET Core 1.x 平台的计划执行时间,满打满算也就两年多一点点。对于这个事实上非常复杂的系统来说,这一路走下来并不容易,道路上出现点曲折还是可以接受的。放眼开源社区,别家项目遇到类似问题也是家常便饭。微软采取了完全开源的方式来打造这个平台,将自己研发过程的每一步都暴露在大家眼皮底下,这种勇气本身就非常令人钦佩了。

至于部分微软老用户抱怨开源之后版本太多,很难跟上研发进度获取最新信息,微软的“首席布道师” Scott Hanselman 打了一个非常形象的比方。绝大多数人吃牛排,都会选择七分或者全熟之类的做法,好比开发工具的用户使用发布候选版本或者正式版本。只是如果遇到一个快速迭代的项目,尤其是频繁发布的开源项目,它的一切版本包括测试版本都是公开的,那么用户一不小心试玩测试版本,就好比吃上五分熟的牛排或者干脆牛肉刺身,有人吃着不舒服或者出现消化不良,那也是很正常的事情。开源的好处,便是大家各取所需,按照各自的习惯去选择适合的版本。不得不说,让用户慢慢接受和认同 .NET Core 的开发模式,还需要一段比较长的时间。

面向未来

.NET Core 1.0 和 1.1 虽然是微软提供官方技术支持的正式版本,为后续版本奠定基础的功劳毋庸置疑,但是严格意义上说,它们仅仅适合一些行业用户,而不是之前使用 .NET Framework 许多年的庞大用户群。因此微软还必须竭尽所能在 .NET Core 2.0 发布之前做出更多改进。

首先,微软一直给予厚望的 .NET Standard 标准,在它的 1.x 版本中仅仅规定了一万多个 API。这使得依赖项众多的庞大软件系统难于迁移到这个新平台。通过拜访自己的客户,尤其是企业客户,微软对于 .NET Standard 2.0 需要包含哪些 API 进行了摸底调查,最终纳入到 2.0 标准中 的 API 数量超过了三万个。

其次,微软完成了对 Xamarin 的收购并接手 Mono 平台,对外加强和 Unity 的合作关系,对内协调之前旗下的 .NET Framework 和 UWP 两个平台,保证 .NET Standard 2.0 能够在这些主流平台上得到完善的支持。这种跨平台兼容性对于所有 .NET 开发者都特别重要,尤其是 Unity 开发者的福音。虽然 Unity 在游戏引擎领域非常热门,但是技术上一系列原因使得它一直无法支持最新的 .NET 技术(如编译器和 IDE)。2016 年 4 月 1 日 Unity 加入 .NET 基金会,这一尴尬局面大为改观。

此外,微软自己旗下的产品也结束了过去几十年间那种每隔几年才发布新版本的慢节奏,走上了小步快跑频繁更新的快速路,这样就能更好地配合 .NET Core 开源项目的发布周期。这里特别表扬一下往日升级慢吞吞的 Visual Studio。Visual Studio 2017 从 2017 年 3 月正式发布起,每隔数周就有新版本发布,版本号从 15.0 一路递增到年底的 15.5,很好地配合了 .NET Core 2.0 测试版本和正式版的发布,还赶上了 Windows 10/UWP 快速升级的节奏。

当然另一个改变,是微软在产品发布时的低调。2017 年不论是 .NET Core 2.0 的几个测试版本还是正式版本,都没有搞隆重的发布会。低调,并不意味着微软忽视自己的产品和使用它的开发者,相反持续改善开发者体验变成了日常重点。想看看哪些新的 API 可以使用,开发者可以查阅 API Browser 网站和 GitHub 相关仓库。想参考新的工具和入门指南,开发者可以访问微软全新的文档网站。想了解哪些新的特性将在后续版本中出现,开发者可以通过微软在 GitHub 主仓库上的公告和 .NET 官方博客提前获知。而绝大部分的资料,都托管在对应的 GitHub 仓库里,不仅官方时时修订和更新,还接受大量来自社区的新内容。

开发者社区对于这个新平台的响应也越来越积极。更多的函数库开始迁移到 .NET Standard 2.0。AppVeyor 等平台也都正式支持了 Visual Studio 2017 和 .NET Core 2.0 SDK。2017 年 5 月 10 日微软发布了新产品 Visual Studio for Mac,填补自身在 Mac 平台开发工具领域的空白,但一时还无法提供适合 Linux 平台的类似工具。同为 .NET 基金会成员的 JetBrains 公司则在 2017 年夏天推出了 Rider 跨平台 IDE 的首个正式版本,补上了这个短板。

从 2002 年 2 月 13 日 .NET Framework 1.0 和 Visual Studio .NET 2002 发布算起,到今年 2 月 13 日,.NET 已经走过十六个年头,迈入了成年阶段。按照官方计划,.NET Core 2.1 将在今年晚些时候发布,在性能和开发者体验等方面都会有大的提升,达成又一个里程碑。雄关漫道真如铁,而今迈步从头越。在微软公司和开源社区等多股力量的不懈努力之下,.NET 必将锐不可当,再创辉煌!

作者介绍

Lex Li,资深软件工程师兼播客主播,微软最有价值技术专家,著有《.NET 传奇》等书。现就职于摩根士丹利蒙特利尔研发中心,从事企业私有云相关的技术推广工作。业余时间里他的身影也会出现在一些微软技术的开源项目或是 Stack Overlow 等在线社区。他的微博帐号是 @Lex_Li,个人博客位于 https://blog.lextudio.com, 播客 .NET FM 位于 http://podcast.sxl.cn。

今日荐文

点击下方图片即可阅读

.NET成年了,然后呢?

中年程序员都在想什么?

《9 小时搞定微信小程序开发》现已更新 35 讲,点击下方图片免费观看其中 10 讲。

相关文章:

  • android 线程消息深入
  • ios动态库和静态库
  • Chrome开发——第一个博客链接插件
  • RabbitMQ消息队列(九):Publisher的消息确认机制
  • 减治算法求n个数中的最小数的位置
  • spark2.1.0 自定义AccumulatorV2累加少值(线程不安全)?
  • heartbeat+ldirectord实现web与dns的高可用性
  • __new__ 是什么鬼
  • C#面向对象20 序列化和反序列化
  • SecureCRT 只用 RZ 命令上传大文件失败
  • Ubuntu 10.04下安装libgtk2.0-dev
  • MySQL多实例介绍及配置
  • Java类与对象初始化的过程(一道经典的面试题)
  • EF架构~性能高效的批量操作(Insert篇)
  • user-agent 验证移动端请求
  • 深入了解以太坊
  • 0基础学习移动端适配
  • avalon2.2的VM生成过程
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • export和import的用法总结
  • golang中接口赋值与方法集
  • JavaScript对象详解
  • Spring Boot MyBatis配置多种数据库
  • TypeScript实现数据结构(一)栈,队列,链表
  • Vue学习第二天
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 我这样减少了26.5M Java内存!
  • 学习Vue.js的五个小例子
  • 追踪解析 FutureTask 源码
  • 带你开发类似Pokemon Go的AR游戏
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • #1015 : KMP算法
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #数学建模# 线性规划问题的Matlab求解
  • (C语言)fread与fwrite详解
  • (五)Python 垃圾回收机制
  • *p++,*(p++),*++p,(*p)++区别?
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET简谈设计模式之(单件模式)
  • .php文件都打不开,打不开php文件怎么办
  • /dev/sda2 is mounted; will not make a filesystem here!
  • @GetMapping和@RequestMapping的区别
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [C\C++]读入优化【技巧】
  • [javaSE] GUI(Action事件)
  • [LeetBook]【学习日记】数组内乘积
  • [leetcode] 66. 加一
  • [LeetCode] NO. 169 Majority Element
  • [leetcode]Flatten Binary Tree to Linked List
  • [luoguP1666] 前缀单词(DP)
  • [POI2009]WIE-Hexer