五大 .NET Framework 4.5 特性






4.91/5 (356投票s)
在本文中,我精选了 .NET 4.5 核心框架中引入的五项我最喜欢的特性。
目录
- 引言
- 特性 1:async 和 await(代码标记)
- 特性 2:Zip 压缩功能(Zip 压缩)
- 特性 3:Regex 超时(TimeOut)
- 特性 4:Profile 优化(改进的启动性能)
- 特性 5:垃圾回收器(GC 后台清理)
- 另外三个值得探索的特性
- 参考文献
引言
.NET 4.5 发布至今已近一年。但大多数近期微软发布版本的问题在于与 .NET 开发者的沟通。开发者仅知道一两个特性,而其他特性则淹没在 MSDN 中,最终沦为简单的文档。
例如,当你问一位 .NET 开发者 .NET 4.5 核心框架有哪些新特性时,大多数人只会说 async
和 await
(至少我接触过的人只谈论过这些特性)。
同样,要全面了解所有新特性也很困难。因为这些特性可能根据你当前的工作内容而显得不那么有趣。
因此,在本文中,我精选了 .NET 4.5 核心框架中引入的五项我最喜欢的特性。同样,我的喜好可能不是你的喜好。但我在选择这些特性时,考虑了更广泛的 .NET 社区,希望我能达到这个期望。
注意:本文不讨论 ASP.NET、WCF、WPF、WWF 等的新特性。它只讨论与核心相关的部分。
特性 1:async 和 await(代码标记)
这项特性已经被过度宣传了,所有的 .NET 传道者都在谈论它。但它仍然是我的最爱,你很快就会明白为什么。
async
和 await
是标记,用于标记代码中应该在任务(线程)完成后恢复执行的位置。
让我们通过理解下面的代码来理解上述声明。如果你查看下面代码的流程
Method()
从Static void main()
入口点被调用。Method()
启动一个Task
(线程)LongTask
,它会等待 10 秒。- 同时,控制权会回到
Method()
来执行任务调用后的剩余代码。换句话说,由于调用是多线程的(Task.Run
…),LongTask
也在运行,即等待 10 秒,同时你Method()
的剩余代码也在执行。
现在,在某些情况下,我们希望步骤 3 的行为有所不同。我们希望在 LongTask()
完成执行后,控制权应该返回到 Method
来执行剩余的代码。async
和 await
关键字有助于实现上述行为。
现在有三点关于 async
和 await
关键字的重要事项需要记住
async
和await
是配对关键字。你不能单独使用它们。async
标记在一个方法上。这个关键字只是一个指示,表明该方法将包含await
关键字。await
关键字标记了任务应该从何处恢复的位置。所以你总会发现这个关键字与Task
一起使用。
下面是前面讨论过的代码的修改版本,我们应用了 async
和 await
。所有其他步骤保持不变,但“步骤 3”在“步骤 2”完成后执行。简单地说,任务完成后,控制权会返回到 Method()
。
现在你已经了解了“async”和“await”,让我问一个反问。上述行为也可以通过使用 Task.Wait
或 Task.ContinueWith
来实现,那么它们有什么区别呢?这个问题我留给你作为作业。
特性 2:Zip 压缩功能(Zip 压缩)
Zip 是最受欢迎的压缩文件格式之一。Zip 格式几乎在所有操作系统上都得到了支持,并内置了相应的名称。
- 在 Windows 操作系统中,它以“Compressed folders”的名称实现。
- 在 MAC OS 中,它以“Archive utility”的名称实现。
现在,.NET 中没有内置的 Zip 压缩实现支持。许多开发者使用了第三方组件,如“DotnetZip”。在 .NET 4.5 中,Zip 功能已集成到框架本身,位于 System.IO.Compression
命名空间下。
第一步是引用两个命名空间
System.IO.Compression.FileSystem
System.IO.Compression
下一步是导入以下两个命名空间
using System.IO.Compression;
如果你想压缩一个文件夹中的文件,可以使用 CreateFromDirectory
函数,如下所示。
ZipFile.CreateFromDirectory(@"D:\data",@"D:\data.zip");
如果你想解压缩,可以使用 ExtractToDirectory
函数,如下面的代码所示。
ZipFile.ExtractToDirectory(@"D:\data.zip", @"D:\data\unzip");
特性 3:Regex 超时(TimeOut)
“Regex”一直是执行验证的首选方式。如果你不熟悉 Regex,请 观看 Regex 视频,我将在其中解释 Regex 的实现方式。但是,由于 Regex 特有的解析逻辑,它容易受到 DOS 攻击。让我们详细了解一下我的意思。
例如,考虑这个正则表达式 - “^(\d+)$”。这个正则表达式表示它只能包含数字。你还可以看到 Regex 的符号图,它显示了 Regex 的评估方式。现在,假设我们要验证“123456X”。它将有六个路径,如图所示。现在,如果我们在它后面加上一个数字,它将需要七个路径。换句话说,随着长度的增加,Regex 需要更多的时间来评估。换句话说,评估所需的时间与字符长度成正比。
但是,如果我们在此基础上增加一个数字,它将需要七个路径。换句话说,随着长度的增加,Regex 需要更多的时间来评估。换句话说,评估所需的时间与字符长度成正比。
现在,让我们将之前定义的 Regex 从“^(\d+)$”复杂化为“^(\d+)+$”。如果你看到 Regex 的符号图,它相当复杂。如果我们现在尝试验证“123456X”,它将经过 32 个路径。如果你再增加一个字符,路径的数量将变为 64。
换句话说,对于上面的 Regex,评估所需的时间随字符数量呈指数级增长。
现在,你可能会问,这有什么关系?这种线性的评估时间增长可能会被黑客利用来进行 DOS(拒绝服务)攻击。他们可以输入一个非常长的字符串,导致你的应用程序永远挂起。
正确的解决方案应该是对 Regex 操作设置超时。好消息是,在 .NET 4.5 中,你现在可以定义一个超时属性,如下面的代码所示。这样,如果你收到任何恶意字符串,应用程序就不会永远陷入循环。
try
{
var regEx = new Regex(@”^(\d+)+$”, RegexOptions.Singleline, TimeSpan.FromSeconds(2));
var match = regEx.Match(“123453109839109283090492309480329489812093809x”);
}
catch (RegexMatchTimeoutException ex)
{
Console.WriteLine(“Regex Timeout”);
}
下面是一个发布在 Facebook 上的精彩视频,它演示了正则表达式的 DOS 攻击以及如何使用 .NET 4.5 的 Regex 超时功能来防止它。不要错过,值得一看。
特性 4:Profile 优化(改进的启动性能)
我们都知道 .NET 代码是半编译格式。在运行时,JIT(Just-in-Time)编译器运行并将半编译的 IL 代码转换为本地机器码。JIT 的一个主要缺点是,当 .NET 应用程序第一次运行时,它运行缓慢,因为 JIT 正在忙于将 IL 代码转换为机器码。
为了缩短这个启动时间,在 .NET 4.5 中,我们有了所谓的“Profile 优化”。Profile 只是一个简单的文件,其中包含应用程序在启动时需要的方法列表。因此,当应用程序启动时,一个后台 JIT 会运行,并开始将这些方法的 IL 代码转换为机器/本地代码。
这种后台 JIT 编译启动方法发生在多个处理器上,从而进一步最小化了启动时间。另外请注意,你需要一个多核机器才能实现 Profile 优化。如果你没有多核机器,则会忽略此设置。
为了创建“Profile”文件,你首先需要导入 System.Runtime
命名空间。然后,你可以调用静态类 ProfileOptimization
的 SetProfileRoot
和 StartProfile
方法。现在,当应用程序启动后台 JIT 时,它将从 Profile 文件中读取并在后台编译你的启动方法,从而缩短启动时间。
using System.Runtime;
// Call the Setprofilerroot and Startprofile method
ProfileOptimization.SetProfileRoot(@"D:\ProfileFile");
ProfileOptimization.StartProfile("ProfileFile");
一个重要提示:Profileoptimization
默认启用 ASP.NET 4.5 和 Silverlight 5 应用程序。所以对于这些技术,不需要编写上面的代码。
特性 5:垃圾回收器(GC 后台清理)
垃圾回收器是 .NET 应用程序中的一项非常繁重的工作。当它是一个 ASP.NET 应用程序时,它的工作量会更大。ASP.NET 应用程序运行在服务器上,大量客户端向服务器发送请求,从而创建大量的对象,使得 GC 在清理不需要的对象时非常辛苦。
在 .NET 4.0 中,当 GC 进行清理时,所有应用程序线程都会被挂起。你可以在上图中看到有三个应用程序线程在运行。我们有两个 GC 在单独的线程上运行。每个逻辑处理器一个 GC 线程。现在,应用程序线程运行并执行其工作。当这些应用程序线程执行其任务时,它们也会创建托管对象。
在某个时刻,后台 GC 会运行并开始清理。当这些后台 GC 开始清理时,它们会挂起所有应用程序线程。这使得服务器/应用程序在那一刻的响应性降低。
为了解决上述问题,引入了服务器 GC。在服务器 GC 中,会创建另一个在后台运行的线程。这个线程在后台工作,并不断清理第 2 代(观看此视频了解 GC 第 0、1、2 代)对象,从而最小化了主 GC 线程的负载。由于有两个 GC 线程在运行,主应用程序线程的挂起时间会减少,从而提高了应用程序吞吐量。要启用服务器 GC,我们需要使用 gcServer
XML 标签并将其设置为 true
。
<configuration>
<runtime>
<gcServer enabled="true"/>
</runtime>
</configuration>
另外三个值得探索的特性
设置 App Domain 的默认区域性
在 .NET 的早期版本中,如果我想设置区域性,我需要在每个线程中进行设置。下面是一段代码示例,展示了在线程级别设置区域性的痛苦。对于拥有大量多线程应用程序的情况,这真是痛苦万分。
CultureInfo cul = new CultureInfo(strCulture);
Thread.CurrentThread.CurrentCulture = cul;
Thread.CurrentThread.CurrentUICulture = cul;
在 4.5 中,我们可以在 App Domain 级别设置区域性,该 App Domain 内的所有线程都将继承该区域性。下面是一个关于如何实现 DefaultThreadCurrentCulture
的示例代码。
CultureInfo culture = CultureInfo.CreateSpecificCulture("fr-FR");
CultureInfo.DefaultThreadCurrentCulture = culture;
数组支持大于两千兆字节的大小
我不确定在什么场景下我们需要一个 2 GB 的集合。所以个人认为我们不需要这个特性。如果我需要这么大的集合,我会将其分解。但我确信这个特性在框架中启用肯定有其充分的理由。
控制台的 Unicode 支持
我将这个特性排除在讨论之外,因为很少有人使用控制台应用程序。我见过人们在学术目的中使用控制台。总而言之,现在控制台应用程序也支持 Unicode 了。
参考文献
- http://msdn.microsoft.com/en-us/library/ms171868.aspx
- Sukesh marla 先生关于 ASP.NET 4.5 新特性的精彩文章
<movie height="360" width="630">
如需进一步阅读,请观看以下面试准备视频和分步视频系列。