65.9K
CodeProject 正在变化。 阅读更多。
Home

五大 .NET Framework 4.5 特性

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (356投票s)

2013 年 5 月 29 日

CPOL

10分钟阅读

viewsIcon

833991

在本文中,我精选了 .NET 4.5 核心框架中引入的五项我最喜欢的特性。

目录

引言

.NET 4.5 发布至今已近一年。但大多数近期微软发布版本的问题在于与 .NET 开发者的沟通。开发者仅知道一两个特性,而其他特性则淹没在 MSDN 中,最终沦为简单的文档。

例如,当你问一位 .NET 开发者 .NET 4.5 核心框架有哪些新特性时,大多数人只会说 asyncawait(至少我接触过的人只谈论过这些特性)。

同样,要全面了解所有新特性也很困难。因为这些特性可能根据你当前的工作内容而显得不那么有趣。

因此,在本文中,我精选了 .NET 4.5 核心框架中引入的五项我最喜欢的特性。同样,我的喜好可能不是你的喜好。但我在选择这些特性时,考虑了更广泛的 .NET 社区,希望我能达到这个期望。

注意:本文不讨论 ASP.NET、WCF、WPF、WWF 等的新特性。它只讨论与核心相关的部分。

特性 1:async 和 await(代码标记)

这项特性已经被过度宣传了,所有的 .NET 传道者都在谈论它。但它仍然是我的最爱,你很快就会明白为什么。

asyncawait 是标记,用于标记代码中应该在任务(线程)完成后恢复执行的位置。

让我们通过理解下面的代码来理解上述声明。如果你查看下面代码的流程

  1. Method()Static void main() 入口点被调用。
  2. Method() 启动一个 Task(线程)LongTask,它会等待 10 秒。
  3. 同时,控制权会回到 Method() 来执行任务调用后的剩余代码。换句话说,由于调用是多线程的(Task.Run…),LongTask 也在运行,即等待 10 秒,同时你 Method() 的剩余代码也在执行。

现在,在某些情况下,我们希望步骤 3 的行为有所不同。我们希望在 LongTask() 完成执行后,控制权应该返回到 Method 来执行剩余的代码。asyncawait 关键字有助于实现上述行为。

现在有三点关于 asyncawait 关键字的重要事项需要记住

  1. asyncawait 是配对关键字。你不能单独使用它们。
  2. async 标记在一个方法上。这个关键字只是一个指示,表明该方法将包含 await 关键字。
  3. await 关键字标记了任务应该从何处恢复的位置。所以你总会发现这个关键字与 Task 一起使用。

下面是前面讨论过的代码的修改版本,我们应用了 asyncawait。所有其他步骤保持不变,但“步骤 3”在“步骤 2”完成后执行。简单地说,任务完成后,控制权会返回到 Method()

现在你已经了解了“async”和“await”,让我问一个反问。上述行为也可以通过使用 Task.WaitTask.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 命名空间。然后,你可以调用静态类 ProfileOptimizationSetProfileRootStartProfile 方法。现在,当应用程序启动后台 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 了。

参考文献

<movie height="360" width="630">

如需进一步阅读,请观看以下面试准备视频和分步视频系列。

© . All rights reserved.