深入理解.NET Core的组件与版本

在开始深入探讨.NET Core之前,首先需要明确讨论的组件:.NET运行时(CLR)、基础类库(BCL)以及C#语言(包括VB.NET)都是独立版本化的,但通常与.NET Framework一起发布,并且常常伴随着新的Visual Studio版本。

.NET Framework

.NET Framework是一个由微软开发的应用程序开发平台,它允许开发者创建Windows应用程序。它包括.NET运行时(CLR)和基础类库(BCL),以及多种编程语言,如C#和VB.NET。

Visual Studio是一个由微软开发的集成开发环境(IDE),它支持多种编程语言,包括C#、VB.NET和C++。Visual Studio与.NET Framework紧密集成,提供了丰富的工具和功能,帮助开发者构建、测试和部署.NET应用程序。

版本 发布年份 .NET运行时 基础类库 C#语言 重大新特性
1.0 2002 1.0 1.0 1.0 -
1.1 2003 1.1 1.1 1.2 -
2.0 2005 2.0 2.0 2.0 泛型
3.0 - 2.0 3.0 2.0 WPF
3.5 2008 2.0 3.5 3.0 LINQ
4.0 2010 4.0 4.0 4.0 dynamic关键字、可选参数
4.5 2012 4.0 4.5 5.0 async关键字
4.5.1 2013 4.0 4.5.1 5.0 -
4.6 2015 4.0 4.6 6.0 空条件运算符

在Visual Studio项目中选择.NET Framework版本时,实际上是在设置CLR和BCL的版本,而不是编译器。这意味着可以使用较新的Visual Studio版本编译代码,使其在旧的.NET Framework版本上运行,同时仍然使用新的C#/VB语言特性。例如,使用Visual Studio 2008分发的C# 3.0编译器,可以使用自动属性(编译器生成的属性后端字段),同时生成的输出将在.NET Framework 2.0上运行。

语言集成查询(LINQ)

LINQ是.NET Framework 3.5中引入的一个特性,它增加了使用函数式编程风格的能力,并将C#/VB表达式转换为查询语言,如SQL。这需要一些先决条件特性的支持:Lambda表达式、扩展方法、查询语法和表达式树。

Lambda表达式本质上是匿名方法的更简洁语法。在C# 3.0之前,匿名方法的写法如下:

myList.ForEach( delegate(string x) { Console.WriteLine(x); });

现在,可以这样写:

myList.ForEach(x => Console.WriteLine(x));

Lambda表达式是C# 3.0引入的编译器特性,不需要对CLR或BCL进行更改。注意,Lambda版本的代码示例没有显式指定string类型,这是由于类型推断。LINQ利用这一点使长方法链和函数式转换更加易读。

扩展方法允许定义新的方法,这些方法看起来像是现有类型的一部分,而无需修改它们。定义一个常规的静态方法,在静态类中,并使用this关键字注释方法的第一个参数,如下所示:

static class MyExtensions { public static int CountOccurencesOfLetter(this string value, char letter) { return value.Split(letter).Length - 1; } }

现在,任何这种第一个参数类型的实例都似乎有一个方法,其余参数作为实例方法:

string word = "banana"; Console.WriteLine(word.CountOccurencesOfLetter('a'));

扩展方法的调用被C#3.0编译器编译为常规静态方法调用,这使得它成为一种语法糖。BCL中添加了一个新的属性ExtensionAttribute,编译器使用它来标记和检测编译代码中的扩展方法。因此,如果要反编译上面定义的代码,结果将如下所示:

static class MyExtensions { [Extension] public static int CountOccurencesOfLetter(string value, char letter) { return value.Split(letter).Length - 1; } } // ... string word = "banana"; Console.WriteLine(MyExtensions.CountOccurencesOfLetter(word, 'a'));

LINQ使用扩展方法为IEnumerable接口添加了大量方法,而不需要每个接口实现自己提供这些方法。

查询语法允许用更像SQL的语法替换方法链:

var result = list.Where(x => x.Id > 2).Select(x => x.Name);

可以替换为:

var result = from x in list where x.Id > 2 select x.Name;

这个C# 3.0特性只要list提供适当的方法签名Where()、Select()等(直接或通过扩展方法),就可以工作。它不依赖BCL中的任何特定类型。

表达式树是LINQ的最后一个组成部分。当一个Lambda表达式被分配给一个变量或方法参数类型Expression时,编译器会生成一个树状数据结构,表示代码将执行的操作,而不是将其编译为实际的IL代码。LINQ使用表达式树将IQueryable上的方法调用转换为SQL查询,而不是像IEnumerable那样直接执行指定的Lambda。

现在已经看到了LINQ能为做什么,不想错过它。但对于部署工具,如Zero Install及其引导程序,重要的是EXE能够在尽可能多的机器上即开即用。这意味着仍然有优势将目标定为.NET Framework版本2.0。

幸运的是,可以两全其美。出色的LinqBridge项目将大部分LINQ回溯到.NET 2.0。它通过利用文章开头提到的C#/VB编译器和BCL的独立版本化来实现这一点。当使用依赖BCL类的特性时,编译器不会在特定程序集中查找。相反,它在所有引用的程序集中查找特定的命名空间。LinqBridge库提供了诸如ExtensionAttribute和IEnumerable扩展方法之类的替代实现。通过将目标定为.NET Framework2.0 BCL并引用LinqBridge与C# 3.0编译器,得到了LINQ支持,而不需要依赖目标机器上的.NET Framework 3.5。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485