升级.Net Core RC2的那些事(四)——TFS2015的CI集成

这篇应该是这个系列的最后一篇了

配置生成代理

配置dotnet cli环境

这步,需要在生成代理的机器上配置cli环境,与本地配置方法一致,可以自行Google

下载及参考地址:

https://www.microsoft.com/net/core#windows

配置环境变量

在生成代理的机器上

  1. 右键 此电脑 (我的电脑)
  2. 点 属性
  3. 点击 高级系统设置
  4. 点击 环境变量 按钮
  5. 新建 一个新的环境变量 名称为:ASP.NET_Core;值为:RC2;如图
  6. 重启生成代理

确认是否设置成功

  1. 登录TFS
  2. 点击 管理项目 (即 右上方的齿轮)
  3. 点击 DefaultCollection (或者你的其他团队项目名)
  4. 点击 代理队列
  5. 看到 代理 -> 功能中 有刚才设置的RC2,就算成功了,如图

此步,主要是对安装配置过RC2的代理进行区分,让TFS进行CI时能选择到有RC2环境的机器

生成定义

这里我们新建一条生成定义,用 空模板 就好

生成步骤

首先,我们需要通过cli把包还原出来

点击 添加生成步骤,实用工具 -> 命令行

设置项中

工具填:dotnet

参数填:restore

其实相当于命令 dotnet restore

接着,我们需要把nuget的包打包,由于涉及几个项目,我这里使用的是PowerShell

点击 添加生成步骤,实用工具 -> PowerShell

脚本文件名为:RunPack.ps1

内容为:

dotnet pack LSW.Weixin\src\LSW.Weixin -c release
dotnet pack LSW.Weixin\src\LSW.Weixin.MP -c release
dotnet pack LSW.Weixin\src\LSW.Weixin.MP.MvcExtension -c release
dotnet pack LSW.Weixin\src\LSW.Weixin.QY -c release

PS:LSW.Weixin\src\LSW.Weixin 这些是我的项目的存储库相对路径,project.json的对应文件夹,可参照修改。我这里完全是把PowerShell当批处理用了,如果有这方面路过的PowerShell大神看到,有好的建议,还请赐教

然后,需要对ASP.Net Core的项目进行发布,同样

点击 添加生成步骤,实用工具 -> PowerShell

脚本文件名为:RunPublish.ps1

内容为:

dotnet publish 微信企业号\src\分销系统 -r win8-x64 -c release
dotnet publish 微信企业号\src\微信企业号 -c release

同样需要修改对应的路径

这里还需要注意的是,由于项目名存在中文,这里的PowerShell脚本需要用 GBK 编码保存,用 UTF-8 编码的话会乱码报错

最后是添加 复制并发布生成项目 的生成步骤

这个和原来一样就不说了

PS:以上的步骤我没有使用 dotnet build 是因为 dotnet pack 和 dotnet publish 都会执行一次build操作,就没必要加这一步了

存储库设置

选自己对应项目的 Git 分支

常规设置

在常规设置选项卡中,增加一个RC2的需求条件,如图

其他选项

根据自己的喜好设置吧

测试生成定义

设置完成后点击 保存

然后点击 为生成排队 就可以测试一下生成定义了

其他补充

TFS的cli会把一些编译警告,当成错误,导致CI无法顺利通过

这里有两种处理办法

1、按标准修改代码,让警告不出现
2、修改project.json,忽略掉相应的警告,具体位置在 buildOptions 配置节 nowarn 下
	"buildOptions": {
		"emitEntryPoint": true,
		"preserveCompilationContext": true,
		"nowarn": [ "CS0168", "CS0169", "CS1998" ]
	},
CI完成后的自动发布Azure、FTP什么的这些没改,可以参考其他文章进行配置

升级.Net Core RC2的那些事(三)——Entity Framework升级

第三篇是Entity Framework升级

修改project.json

把原来 EntityFramework 的包 换成 Microsoft.EntityFrameworkCore

版本从 7.0.0-rc1-final 改为 1.0.0-rc2-final

对照表如下:

RC1 Package RC2 Equivalent
EntityFramework.MicrosoftSqlServer 7.0.0-rc1-final Microsoft.EntityFrameworkCore.SqlServer 1.0.0-rc2-final
EntityFramework.SQLite 7.0.0-rc1-final Microsoft.EntityFrameworkCore.SQLite 1.0.0-rc2-final
EntityFramework7.Npgsql 3.1.0-rc1-3 NpgSql.EntityFrameworkCore.Postgres <to be advised>
EntityFramework.SqlServerCompact35 7.0.0-rc1-final EntityFrameworkCore.SqlServerCompact35 1.0.0-rc2-final
EntityFramework.SqlServerCompact40 7.0.0-rc1-final EntityFrameworkCore.SqlServerCompact40 1.0.0-rc2-final
EntityFramework.InMemory 7.0.0-rc1-final Microsoft.EntityFrameworkCore.InMemory 1.0.0-rc2-final
EntityFramework.IBMDataServer 7.0.0-beta1 Not yet available for RC2
EntityFramework.Commands 7.0.0-rc1-final Microsoft.EntityFrameworkCore.Tools 1.0.0-preview1-final
EntityFramework.MicrosoftSqlServer.Design 7.0.0-rc1-final Microsoft.EntityFrameworkCore.SqlServer.Design 1.0.0-rc2-final

增加EF cli工具

在 project.json 的 tools 配置节中加入

		"Microsoft.EntityFrameworkCore.Tools": {
			"version": "1.0.0-preview1-final",
			"imports": [
				"portable-net45+win8+dnxcore50",
				"portable-net45+win8"
			]
		}

EF的相关cli命令,由原来的 dnx ef 改为 dotnet ef,具体可以通过 dotnet ef --help 来查看

修改代码中的命名空间

把原来的 Microsoft.Data.Entity 改为 Microsoft.EntityFrameworkCore

这里可以批量查找替换掉

修改Startup.cs

RC2中已经移除了AddEntityFramework()、AddInMemoryDatabase()、AddSqlServer(),所以我们也要在代码中相应的移除掉它们,以我自己的项目中为例子

原来为:

		public void ConfigureServices(IServiceCollection services)
		{
#if DEBUG
			services.AddEntityFramework()
				.AddInMemoryDatabase()
				.AddDbContext<EFContext>(option => {
					option.UseInMemoryDatabase();
				});
#else
			services.AddEntityFramework()
				.AddSqlServer()
				.AddDbContext<EFContext>(option => {
					option.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
				});
#endif
			services.AddApplicationInsightsTelemetry(Configuration);
			// Add framework services.
			services.AddMvc();
		}

现在则改为:

		public void ConfigureServices(IServiceCollection services)
		{
#if DEBUG
			services.AddDbContext<EFContext>(option =>
			{
				option.UseInMemoryDatabase();
			});
#else
			services.AddDbContext<EFContext>(option =>
			{
				option.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
			});
#endif
			// Add framework services.
			services.AddMvc();
		}

 

升级.Net Core RC2的那些事(二)——升级ASP.Net Core项目

升级完类库项目,第二篇,我们来升级ASP.Net Core项目

修改global.json与project.json

这里可以参照,升级.Net Core RC2的那些事(一)

这里补充一点就是如果你觉得这样修改复杂,你完全可以新建一个项目,把这两个文件拷贝过来,再加上自己引用过的包,也是可以的,看你觉得那种方法更简单点

增加Program入口类

Program.cs:

	public class Program
	{
		public static void Main(string[] args)
		{
			var host = new WebHostBuilder()
				.UseKestrel()
				.UseContentRoot(Directory.GetCurrentDirectory())
				.UseIISIntegration()
				.UseStartup<Startup>()
				.Build();

			host.Run();
		}
	}

并移除Startup.cs中的

		// Entry point for the application.
		public static void Main(string[] args) => WebApplication.Run<Startup>(args);

修改类名和命名空间名

将所有 Microsoft.AspNet.* 的命名空间修改为 Microsoft.AspNetCore.*

参照下表修改对应的类名

旧类 新类
IWebApplicationBuilder IWebHostBuilder
WebApplicationBuilder WebHostBuilder
IWebApplication IWebHost
WebApplication WebHost
WebApplicationOptions WebHostOptions
WebApplicationDefaults WebHostDefaults
WebApplicationService WebHostService
WebApplicationConfiguration WebHostConfiguration

如果你有使用全局环境变量,也请对照下表修改

Old prefix New prefix
ASPNET_WEBROOT ASPNETCORE_WEBROOT
ASPNET_SERVER ASPNETCORE_SERVER
ASPNET_APP ASPNETCORE_APPLICATIONNAME
ASPNET_ENVIRONMENT ASPNETCORE_ENVIRONMENT
ASPNET_DETAILEDERRORS ASPNETCORE_DETAILEDERRORS

修改Logging

如果你有使用日志功能,需要修改下日志等级的配置,具体在 appsettings.json

例如:

  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },

具体请对照下表:

Old Levels New Levels
Critical Critical
Error Error
Warning Warning
Information Information
Verbose Debug
Debug Trace

关于PostAsJsonAsync与ReadAsAsync

如果以前有使用 Microsoft.AspNet.WebApi.Client 的,当然现在也是可以直接引用这个包来实现Web API之间的对接的

但我在实战中发现,也许是因为引用包之间的版本冲突,会有诡异的异常,于是我自己实现了一个Helper也分享给大家

	public static class HttpClientHelper
	{
		public static async Task<T> ReadAsAsync<T>(this HttpContent content)
		{
			return JsonConvert.DeserializeObject<T>(await content.ReadAsStringAsync());
		}

		public static async Task<HttpResponseMessage> PostAsJsonAsync<T>(this HttpClient client, string url, T model)
		{
			SetHeader(client);
			return await client.PostAsync(url, SetContent(model));
		}

		private static StringContent SetContent<T>(T model)
		{
			return new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json");
		}

		private static void SetHeader(HttpClient client)
		{
			client.DefaultRequestHeaders.Clear();
			client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
			client.DefaultRequestHeaders.AcceptCharset.Add(new StringWithQualityHeaderValue("utf-8"));
		}

		public static async Task<HttpResponseMessage> PutAsJsonAsync<T>(this HttpClient client, string url, T model)
		{
			SetHeader(client);
			return await client.PutAsync(url, SetContent(model));
		}
	}

关于文件上传保存

以前有.SaveAsAsync,这样便捷的扩展方法保存的,现在没有了(或者说藏到了哪里目前找不到了),于是就需要手写

相关的代码片段:

if (model.picdata != null)
			{
				var extName = ContentDispositionHeaderValue.Parse(model.picdata.ContentDisposition).FileName.Trim('"');
				int i = extName.LastIndexOf('.');
				extName = extName.Substring(i);
				string fileName = Guid.NewGuid() + extName;
				var filePath = _hostingEnvironment.WebRootPath + @"\upload\" + fileName;
				//保存文件
				using (var fileStream = new FileStream(filePath, FileMode.Create))
				{
					var inputStream = model.picdata.OpenReadStream();
					await inputStream.CopyToAsync(fileStream);
				}
				//await model.picdata.SaveAsAsync(filePath);
			}

本人的项目中就这一处需要保存文件的,就不封装了:),你喜欢也可以自己封装下

关于System.Drawing

由于项目中需要使用图片处理(生成二维码),好多大神建议另外建项目调用,但我想做在一个项目中,故有此一段

首先,ASP.Net Core项目是不支持System.Drawing的,如果需要使用,则需要修改成纯 net461 的项目,这里需要修改一下project.json

1、删除 Microsoft.NETCore.App 配置节

		"Microsoft.NETCore.App": {
			"version": "1.0.0-rc2-3002702",
			"type": "platform"
		},

2、修改 frameworks 配置节,只保留 net461 项

	"frameworks": {
		"net461": {
			"frameworkAssemblies": {
				"System.Drawing": "4.0.0.0"
			},
			"dependencies": {
				"ThoughtWorks.QRCode": "1.1.0",
				"Microsoft.NETCore.Platforms": "1.0.1-rc2-24027"
			}
		}
	},

这样修改完成后就可以在项目中使用 System.Drawing 命名空间了

副作用:

这样修改后,项目是不能在CoreCLR上跑的,Windows环境下不会存在问题,Linux下则需要配置Mono环境��可以

关于Session

现在光在 ConfigureServices 方法中加 services.AddSession(); 是不够的,实际使用时会报错

还需要在 Configure 方法中加 app.UseSession();

发布到IIS

通过VS发布

现在你可以像MVC5那样通过 右键菜单的 发布 功能,用Web Deploy来发布到IIS,需要注意的是目前这个工具对中文的支持不是很好,所以不要使用中文的项目名哟

通过cli发布

你也可以通过以下命令获得发布文件

dotnet publish -c release

然后把获得的文件通过FTP等途径上传到服务器

PS:此命令需要在 project.json 的同级目录下运行

IIS配置

IIS绑定的目录应该是最多DLL的那一层,而不是以前RC1的wwwroot

应用程序池中设置 无托管代码 等,这些基本和RC1一致

其他补充

如果你的项目存在Areas,默认是不会发布出去的,此时需修改project.json的publishOptions配置节,例如:

	"publishOptions": {
		"include": [
			"wwwroot",
			"Views",
			"Areas/Admin/Views",
			"appsettings.json",
			"web.config"
		]
	},

 

升级.Net Core RC2的那些事(一)——升级RC1的类库项目

微软终于发布了.Net Code RC2了,作为一个软粉当然是第一时间升级了。《升级.Net Core RC2的那些事》系列文章主要是记录本人升级RC2的相关步骤以及遇到过的坑。

第一篇先写类库项目(Nuget包项目)的升级

升级VS工具

这里只提供一个下载地址:.NET Core Tooling Preview 1 for Visual Studio 2015

本人主要使用VS2015开发,升级Update2后,只要装这一个东西就够了,使用VSCode的朋友,或需要手动配置环境的朋友,可以Google相关的文章,这里不做赘述。

修改global.json

如果你项目中没有这个文件,则可以忽略

{
  "sdk": {
    "version": "1.0.0-rc1-update2"
  }
}

修改为

{
	"sdk": {
		"version": "1.0.0-preview1-002702"
	}
}

修改project.json

增加packOptions

原来 tags、projectUrl、licenseUrl 这3个配置项、现在需要放到一个名叫packOptions的节点下

如:

	"tags": [ "weixin", "wechat", "微信" ],
	"projectUrl": "http://blog.lishewen.com/",
	"licenseUrl": "http://blog.lishewen.com/",

现在需要修改为

	"packOptions": {
		"tags": [ "weixin", "wechat", "微信" ],
		"projectUrl": "http://blog.lishewen.com/",
		"licenseUrl": "http://blog.lishewen.com/"
	},

frameworks节的修改

原 net451 或 dnx451 需要修改为 net461

原 dotnet5.4 需要修改为

		"netstandard1.5": {
			"imports": "dnxcore50"
		}

dependencies节的修改

这里主要是修改引用包的版本号

原 1.0.0-rc1-final 或 1.0.0-rc1-update2 的包,修改为 1.0.0-rc2-final

其他包则需要修改成 -rc2-24027 结尾的对应包

发布

RC2的工具现在只会生成DLL,并不会像RC1那样直接生成Nuget包

目前如果需要Nuget的发布包,只能通过cli命令行获得

打包命令:

dotnet pack -c release

PS:此命令需要在project.json文件的同级目录下运行

修复.NET Framework的简单方式

  毫无疑问,对于所有 Windows 的用户和开发者来说,拥有一套正确运作的 .NET Frameworks 非常重要。在开发过程中,使用它可以让意外重写所需的文件变得更加简单。为了更大程度减少故障排除的时间,Microsoft 提供了 .NET Framework Repair Tool。这个工具可以扫描 Windows 系统中的漏洞以及需要安装的 .NET Framework 的包。

  该程序可以通过命令行或是图形化向导程序执行。当没有人看管的情况下,使用命令行开关就可以让工具正常运行,并让合适的 .NET 包出现在网络共享中(相较于需要连接网络的情况)。还可以选择禁用默认功能:当使用工具执行后将诊断日志文件传送到 Microsoft。

  使用的典型操作需要以下步骤:

  1. 扫描已安装的 .NET Framework 中的已知错误,当发现错误时,向用户提供错误列表。
  2. 采用以下任何一条纠正措施:
    • 确保 Windows 安装服务正常运行
    • 重置系统文件夹上的自主访问控制列表
    • 检验并正确更新注册
  3. 如果采取了步骤 2 中的行为还是没有成功解决问题,用户可以选择对已安装的 .NET Frameworks 全修复。
  4. 可选择发送包含系统日志的 CAB 文件给 Microsoft。

  本文作者尝试在自己的机器上运行了这个程序,操作大约进行了 20 分钟左右。完成之后,在 %TEMP%目录下会留下一个 cab 文件,其中包含目录下 AppData 中的日志文件。cab 文件中还包含 HKCR 和 HKLM 的注册转储文件(HKEY_LOCAL_MACHINE)。

  如果想了解有关于这个程序操作的更多细节,可以关注 The .NET Fundamentals Team 的一篇博文和与它的 KB 文章。最新的 V1.3 版本对 .NET Frameworks4.6.1 及之前的版本全部支持。在 Windows 操作系统上通过 Windows 7 Service Pack 1 和 Windows Server 2008 R2 Service Pack 1 支持它的操作。

VS2015 CTP6安装后没有JavaScript模板和编辑器智能提示的解决办法

VS2015 CTP6安装后没有JavaScript模板和编辑器智能提示,修复和卸载重装都不行的话可以试试以下办法:

假设VS2015安装在 F:\Microsoft Visual Studio 14.0

可到C盘下 C:\Program Files (x86)\Microsoft Visual Studio 14.0 复制里面的所有文件及文件夹到 F:\Microsoft Visual Studio 14.0

用管理员身份打开 Developer Command Prompt for VS2015

输入以下命令:

devenv /setup

devenv.exe /InstallVSTemplates

待执行完毕后,重新打开VS2015应该就可以了

Html5视频拼图源码

Html5视频拼图源码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<style>
		body{margin:0;padding:0;}
		.allCanvas{
			position: relative;
			margin:50px auto;
            border:1px solid;
			width:600px;
			height:400px;
		}
		.vcanvas{
			position: absolute;
			display: block;
            box-shadow:#333 0 0 3px;
		}
		.highZindex{
			z-index:50;
		}
	</style>
	<title>视频拼图</title>
</head>
<body>
	<div class="allCanvas">
		<canvas id="lishewen" width="600" height="400" style="display:none"></canvas>
	</div>
	<video id="video" src="tj.mp4" width="600px" height="400px" controls="control" loop="loop" style="display:block;position:absolute;top:-6000px;"></video>
	<script>
		var video = document.getElementById("video");
		var cs = document.getElementById("lishewen");
		var ctx = cs.getContext('2d')
		var rows = 3,
			cols = 3,
			cb = document.querySelector(".allCanvas"),
			vw = 600,
			vh = 400,
			ww = window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth,
			wh = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight,
			maxindex = 10,
			canvases = [];

		function createCanvas(){
			var num = rows*cols;
			for(var i=0;i<cols;i++){
				for(var j=0;j<rows;j++){
					var canvas = new vCanvas(Math.random()*getRandom(-100 , 700), Math.random()*getRandom(0 , wh-vh/cols)-cb.offsetTop , vw/rows , vh/cols , j , i);
					canvases.push(canvas);
				}
			}
		}

		function getRandom(a , b){
			return Math.random()*(b-a) + a;
		}

		var vCanvas = function(x,y,w,h,cols,rows){
			this.x = x;
			this.y = y;
			this.w = w;
			this.h = h;
			this.cols = cols;
			this.rows = rows;
			this.creat();
			this.behavior();
		}
		vCanvas.prototype = {
			creat:function(){
				this.cas = document.createElement("canvas");
				cb.appendChild(this.cas);
				this.cas.className = "vcanvas";
				this.cas.style.left = this.x+"px";
				this.cas.style.top = this.y+"px";
				this.cas.width = this.w;
				this.cas.height = this.h;
				this.cas.style.zIndex = maxindex-1;
			},
			behavior:function(){
				this.cas.onmousedown = function(e){
					e = e || window.event;
					var that = this;
					var om = {
						x:e.clientX,
						y:e.clientY
					}
					if(parseInt(this.style.zIndex) < maxindex){
						this.style.zIndex = maxindex+1;
						maxindex = parseInt(this.style.zIndex);
					}
					window.onmousemove = function(e){
						e = e || window.event;
						var nm = {
							x:e.clientX,
							y:e.clientY
						}
						that.style.left = parseInt(that.style.left.replace("px","")) + (nm.x-om.x) + "px";
						that.style.top = parseInt(that.style.top.replace("px","")) + (nm.y-om.y) + "px";
						om = nm;
					}
					window.onmouseup = function(){
						this.onmousemove = null;
					}
				}
			}
		}

		Array.prototype.forEach = function(callback){
			for(var i=0;i<this.length;i++){
				callback.call(this[i]);
			}
		}

		var lastTime = 0;
		function initAnimate(){
			lastTime = new Date();
			createCanvas();
			animate();
		}

		function animate(){
			var newTime = new Date();
			if(newTime - lastTime > 30){
				lastTime = newTime;
				ctx.drawImage(video , 0 , 0 , vw , vh);

				canvases.forEach(function(){
					var ctx2 = this.cas.getContext('2d');
					ctx2.drawImage(cs , -this.cols*this.w , -this.rows*this.h , vw , vh);
				});
			}
			if("requestAnimationFrame" in window){
				requestAnimationFrame(animate);
			}
			else if("webkitRequestAnimationFrame" in window){
				webkitRequestAnimationFrame(animate);
			}
			else if("msRequestAnimationFrame" in window){
				msRequestAnimationFrame(animate);
			}
			else if("mozRequestAnimationFrame" in window){
				mozRequestAnimationFrame(animate);
			}
		}

		video.play();
		initAnimate();
	</script>
</body>
</html>

福利:Google放出Android 5.0源代码

  Android 5.0 正式版已经亮相,不过大家更关心的还是自己手机何时能用上,现在好消息终于来了。

  谷歌已经正式将 5.0 系统 Lollipop 的源代码放在了 AOSP 上,有了这个开放的源代码后,大神们就可以跟进了

  与此同时,国外媒体报道称,最大的第三方 ROM 团队 CM 已经表示,将跟进 Android 5.0 的适配工作。

  可以肯定的是,未来一、两个月,HTC/LG/三星等安卓厂商已经会让自家吃上棒棒糖的,就算你的机型不在升级之列,也可以期待那些大神们的作品。

  地址:https://android.googlesource.com/platform/build/+/lollipop-release

C# 6和 VB 12的最新特性列表

  随着下个版本的 C# 发布日逐渐临近,那些还没有完成的特性必须被砍掉。最近从特性列表中被砍掉的特性包括主要构造函数(primary constructor)和声明表达式(declaration expression)。Mads Torgersen 写道

正如我之前所说的一样,语言特性在这次发布中只是第二等级的考虑。这次发布的主要目标是出色地完成 Roslyn 价值命题的首个发布,包括在 IDE 中对语言特性更深入的理解,以及让所有人都可以通过一套健壮和完善的 API 进行访问。为了更好地实现这次提交的内容,我们不得不对语言特性进行一些相应的缩减。

  好在并不是只有坏消息,一个之前从未被微软提到过的新特性即将随C# 6 发布,这就是字符串值插入(string interpolation),该特性允许开发者对字符串直接进行内联,而无需显式地调用 String.Format 方法。字符串值插入在 2009 年时曾作为一个实验性特性加入到 Mono 编译器中

  下个版本中将包含的特性包括以下内容:

  • 自动属性的初始化器(VB 中已具有该特性)
  • 只包括 Get 方法的自动属性
  • 在构造函数中为只包括 Get 方法的自动属性传值
  • 无参数的结构体(struct)构造函数
  • 直接访问静态成员(VB 中已具有该特性)
  • Dictionary 的初始化器(仅限于C#)
  • 在 catch 与 finally 中调用 await(C#中已完成,VB 尚未确定)
  • 异常过滤器(VB 中已具有该特性)
  • Typecase(VB 尚未确定)
  • 部分(Partial)模块(仅限于 VB 中,不适用于C#)
  • 部分(Partial)接口(C#中已具有该特性)
  • 多行字符串(C#中已具有该特性)
  • 获取某一年第一天日期的日期表达式(仅限于 VB)
  • 某行代码延续的注释(仅限于 VB,不适用于C#)
  • TypeOf 和 IsNot(仅限于 VB,不适用于C#)
  • 在成员定义中使用表达式(仅限于C#)
  • Null 值传递(C#中已完成,VB 尚未确定)
  • Params 参数接受 IEnumerable 类型(尚未确定)
  • 字符串值插入(已在计划中)
  • nameof 操作符(C#中已完成,VB 已在计划中)
  • #pragma 指令(C#中已具有该特性)
  • 在自动属性中指定字段(尚未确定)
  • 你可以在语言特性状态页面中看到尚未被砍掉的全部特性列表。