搜索
首页后端开发C#.Net教程C# 2.0 Specification(一)简介

19.C#2.0介绍

C#2.0引入了几项语言扩展,其中最重要的是泛型、匿名方法、迭代器和不完整类型(partial type)。

泛型可以让类、结构、接口、委托和方法,通过他们所存储和操纵的数据的类型被参数化。泛型是很有用的,因为他们提供了更强的编译时类型检查,减少了数据类型之间的显式转换,以及装箱操作和运行时类型检查。 
匿名方法可以让代码块以内联的方式潜入到期望委托值的地方。匿名方法与Lisp 编程语言中的λ函数(lambda function)相似。C#2.0支持“closures”的创建,在其中匿名方法可以访问相关局部变量和参数。 
迭代器是可以递增计算和产生值的方法。迭代器让类型指定foreach语句如何迭代它的所有元素,变得很容易。 
不完整类型可以让类、结构和接口被拆分成多个部分存储在不同的源文件中,这更利于开发和维护。此外,不完整类型允许某些类型的机器生成的部分与用户编写的部分之间的分离,因此增加由工具产生的代码很容易。


本章将介绍这些新特征。介绍完之后,接下来的四章提供了这些特征的完整的技术规范。

C#2.0的语言扩展主要被设计用于确保与现存的代码之间最大的兼容性。例如,尽管C#2.0对于where、yield 和partial这些词在特定上下文中赋予了特别的意义,但这些词仍然可被用作标识符。实际上,C# 2.0没有增加任何可能与现有代码中的标识符冲突的关键字。

19.1 泛型

泛型可以让类、结构、接口、委托和方法,通过他们所存储和操纵的数据的类型被参数化。C#泛型对于使用Eiffel或Ada的泛型的用户,或者对于C++模板的用户来说是很熟悉的;但他们将不用再去忍受后者的众多的复杂性。


19.1.1为什么使用泛型

没有泛型的话,通用目的的数据结构可以采用object类型存储任何类型的数据。例如,下面的Stack类在一个object数组中存储数据,而它的两个方法,Push和Pop相应地使用object接收和返回数据。

public class Stack
{
object[] items;
int count;
public void Push(object item){…}
public object Pop(){…}
}

尽管使用类型object可以使得Stack类更加灵活,但这样做也并不是没有缺点。例如,你可以将一个任何类型的值,诸如,Customer的一个实例压入(Push)堆栈。但当你取回一个值时,Pop方法的结果必须被显式地强制转换到合适的类型,为一个运行时类型检查去编写代码,以及带来的性能不利影响,是很令人讨厌的。

Stack stack = new Stack();
Stack.Push(new Customer());
Customer c = (Customer)stack.Pop();

如果一个值类型的值,例如一个int被传递到Push方法,它将会被自动装箱。当后面获得这个int 时,它必须使用一个显式的强制转换而被取消装箱。

Stack stack = new Stack(); 
Stack.Push(3);
int I = (int)stack.Pop();

这种装箱和取消装操作增加了性能开销,因为它们涉及到动态内存的分配和运行时类型检查。

Stack类的更大的问题是,它不能强制放置在堆栈上的数据种类。实际上,Customer实例可以被压入堆栈,而取回它时可能被强制转换到错误的类型。

Stack stack = new Stack();
Stack.Push(new Customer());
String s = (string)stack.Pop();

尽管先前的代码是Stack类的一种不恰当用法,但这段代码从技术上说是正确的,并且也不会报告编译时错误。问题直到代码执行时才会冒出来,在这一点上将会抛出一个InvalidCastException异常。

如果Stack类具有能够指定其元素的类型能力,那么很显然它能从这种能力得到好处。使用泛型,这将会变成可能。

19.1.2 创建和使用泛型

泛型为创建具有类型参数(type parameter)的类型提供了工具。下面的例子声明了一个带有类型参数T的泛型Stack类。类型参数在类名字之后的“c8143eeab66c904ba2ca1dfe843288c3”分界符中指定。这里没有object与别的类型之间的相互转换,Stack8742468051c85b06f0a0af9e3e506b5c的实例接受它们被创建时的类型,并且存储那个类型的数据而没有转换它。类型参数T充当一个占位符,直到使用的时候才指定一个实际的类型。注意,T被用作内部items数组的元素类型、Push方法参数的类型和Pop方法的返回值类型。

Public class Stack<T>
{
T[] items;
int count;
public void Push(T item){…}
public T Pop(){…}
}

当泛型类Stack8742468051c85b06f0a0af9e3e506b5c被使用时,T所代替的实际类型将被指定。在下面的例子中,int 将被作为T的类型参数而给出。

Stack<int> stack = new Stack<int>();
Stack.Push(3);
int x = stack.Pop();

Stackbd43222e33876353aff11e13a7dc75f6类型被称为构造类型(constructed type)。在Stackbd43222e33876353aff11e13a7dc75f6类型中,T的每次出现都被使用类型参数int代替。当Stackbd43222e33876353aff11e13a7dc75f6的实例被创建时,items数组的本地存储就是一个int[]而不是object[],与非泛型Stack相比,它提供了更高的存储效率。同样地,在int值上的Stackbd43222e33876353aff11e13a7dc75f6操作的Push和Pop方法,将会使得压入其他类型的值到堆栈中出现一个编译时错误,并且当取回值的时候也不需要转换回它们原始的类型。

泛型提供了强类型,意义例如压入一个int到Customer对象堆栈将会出现错误。就好像Stackbd43222e33876353aff11e13a7dc75f6被限制只能在int值上操作,同样Stackc214b1127c801bd6a2a45c5b466f54b2也被限制用于Customer对象。

对于下面的例子,编译器将会在最后两行报告错误。

Stack<Customer> stack = new Stack<Customer>();
Stack.Push(new Customer());
Customer c = stack.Pop();
stack.Push(3); //类型不匹配错误
int x = stack.Pop(); //类型不匹配错误

泛型类型声明可以有任意数量的类型参数。先前的Stack8742468051c85b06f0a0af9e3e506b5c例子 只有一个类型参数,但一个通用的Dictionary类可能有两个类型参数,一个用于键(key)的类型,另一个用于值(value)的类型。

public class Dictionary<K , V>
{
public void Add(K key , V value){…}
public V this[K key]{…}
}
当Dictionary<K , V> 被使用时,必须提供两个类型参数。
Dictionary<string , Customer> dict = new Dictionary<string , Customer>();
Dict.Add(“Peter”, new Customer());
Custeomer c = dict[“Perter”];

19.1.3泛型类型实例化

与非泛型类型相似,被编译过的泛型类型也是由中间语言[Intermediate Language(IL)]指令和元数据表示。泛型类型的表示当然也对类型参数的存在和使用进行了编码。

当应用程序首次创建一个构造泛型类型的实例时,例如,Stackbd43222e33876353aff11e13a7dc75f6,.NET公共语言运行时的实时编译器(JIT)将在进程中把泛型IL和元数据转换为本地代码,并且将类型参数替换为实际的类型。对于那个构造泛型类型的后续引用将会使用相同的本机代码。从一个泛型类型创建一个特定构造类型的过程,称为泛型类型实例化(generic type instantiation)。[/b]

.NET公共语言运行时使用值类型为每个泛型类型实例创建了一个本地代码的特定拷贝,但对于所有的引用类型它将共享那份本地代码的单一拷贝(因为,在本地代码级别,引用只是带有相同表示的指针)。

19.1.4约束

一般来讲,泛型类不限于只是根据类型参数存储值。泛型类经常可能在给定类型参数的类型的对象上调用方法。例如,Dictionarybfbf51059cfb0a0d14f2e5a287507931类中的Add方法可能需要使用CompareTo方法比较键值。

public class Dictionary<K , V>
{
public void Add(K key , V value)
{
…
if(key.CompareTo(x)<0){…}//错误,没有CompareTo方法
…
}
}

因为为K所指定的类型参数可能是任何类型,可以假定key参数存在的唯一成员,就是那些被声明为object类型的,例如,Equals,GetHashCode和ToString;因此,在先前例子中将会出现编译时错误。当然,你可以将key参数强制转换到一个包含CompareTo方法的类型。例如,key参数可能被强制转换到IComparable接口。

public class Dictionary<K , V>
{
public void Add(K key , V value)
{
…
if(((IComparable)key).CompareTo(x)<0){…}
…
}
}

尽管这种解决办法有效,但它需要在运行时的动态类型检查,这也增加了开销。更糟糕的是,它将错误报告推迟到了运行时,如果键(key)没有实现IComparable接口将会抛出InvalidCastException异常。

为了提供更强的编译时类型检查,并减少类型强制转换,C#允许为每个类型参数提供一个约束(constraint)的可选的列表。类型参数约束指定了类型必须履行的一种需求,其目的是为了为类型参数被用作实参(argument)。约束使用单词where声明,随后是类型参数的名字,接着是类或接口类型的列表,和可选的构造函数约束new()。

public class Dictionary<K, V> where K :IComparable
{
public void Add(K key , V value)
{
…
if(key.CompareTo(x)<0){…}
…
}
}

给定这个声明,编译器将会确保K的任何类型实参是实现了IComparable接口的类型。

并且,在调用CompareTo方法之前也不再需要对key参数进行显式地强制转换。为类型参数作为一个约束而给出的类型的所有成员,对于类型参数类型的值时直接有效的。

对于一个给定的类型参数,你可以指定任意数量的接口作为约束,但只能有一个类。每个约束的类型参数有一个单独的where 语句。在下面的例子中,类型参数K有两个接口约束,类型参数e有一个类约束和一个构造函数约束。

public class EntityTable<K, E>
where K:IComparable<K>,IPersisable
where E:Entity, new()
{
public void Add(K key , E entity)
{
…
if(key.CompareTo(x)<0){…}
…
}
}

在前面的例子中,构造函数约束new(),确保为E用作类型参数的类型具有一个公有的、无参数构造函数,并且它允许泛型类使用new E()创建该类型的实例。

类型参数约束应该很小心的使用。尽管它们提供了更强的编译时类型检查,在某些情况下增强了性能,但它们也限制了泛型类型的可能的用法。例如,泛型类List8742468051c85b06f0a0af9e3e506b5c可能约束T实现IComparable接口,由此它的Sort方法将可以比较项的大小。然而,这么做却使得没有实现IComparable 接口的类型不能使用List8742468051c85b06f0a0af9e3e506b5c,即使是在这些情形下,Sort方法根本就没有被调用过。

以上就是C# 2.0 Specification(一)简介的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
C#作为多功能.NET语言:应用程序和示例C#作为多功能.NET语言:应用程序和示例Apr 26, 2025 am 12:26 AM

C#在企业级应用、游戏开发、移动应用和Web开发中均有广泛应用。1)在企业级应用中,C#常用于ASP.NETCore开发WebAPI。2)在游戏开发中,C#与Unity引擎结合,实现角色控制等功能。3)C#支持多态性和异步编程,提高代码灵活性和应用性能。

C#.NET用于网络,桌面和移动开发C#.NET用于网络,桌面和移动开发Apr 25, 2025 am 12:01 AM

C#和.NET适用于Web、桌面和移动开发。1)在Web开发中,ASP.NETCore支持跨平台开发。2)桌面开发使用WPF和WinForms,适用于不同需求。3)移动开发通过Xamarin实现跨平台应用。

C#.NET生态系统:框架,库和工具C#.NET生态系统:框架,库和工具Apr 24, 2025 am 12:02 AM

C#.NET生态系统提供了丰富的框架和库,帮助开发者高效构建应用。1.ASP.NETCore用于构建高性能Web应用,2.EntityFrameworkCore用于数据库操作。通过理解这些工具的使用和最佳实践,开发者可以提高应用的质量和性能。

将C#.NET应用程序部署到Azure/AWS:逐步指南将C#.NET应用程序部署到Azure/AWS:逐步指南Apr 23, 2025 am 12:06 AM

如何将C#.NET应用部署到Azure或AWS?答案是使用AzureAppService和AWSElasticBeanstalk。1.在Azure上,使用AzureAppService和AzurePipelines自动化部署。2.在AWS上,使用AmazonElasticBeanstalk和AWSLambda实现部署和无服务器计算。

C#.NET:强大的编程语言简介C#.NET:强大的编程语言简介Apr 22, 2025 am 12:04 AM

C#和.NET的结合为开发者提供了强大的编程环境。1)C#支持多态性和异步编程,2).NET提供跨平台能力和并发处理机制,这使得它们在桌面、Web和移动应用开发中广泛应用。

.NET框架与C#:解码术语.NET框架与C#:解码术语Apr 21, 2025 am 12:05 AM

.NETFramework是一个软件框架,C#是一种编程语言。1..NETFramework提供库和服务,支持桌面、Web和移动应用开发。2.C#设计用于.NETFramework,支持现代编程功能。3..NETFramework通过CLR管理代码执行,C#代码编译成IL后由CLR运行。4.使用.NETFramework可快速开发应用,C#提供如LINQ的高级功能。5.常见错误包括类型转换和异步编程死锁,调试需用VisualStudio工具。

揭开c#.net的神秘面纱:初学者的概述揭开c#.net的神秘面纱:初学者的概述Apr 20, 2025 am 12:11 AM

C#是一种由微软开发的现代、面向对象的编程语言,.NET是微软提供的开发框架。C#结合了C 的性能和Java的简洁性,适用于构建各种应用程序。.NET框架支持多种语言,提供垃圾回收机制,简化内存管理。

C#和.NET运行时:它们如何一起工作C#和.NET运行时:它们如何一起工作Apr 19, 2025 am 12:04 AM

C#和.NET运行时紧密合作,赋予开发者高效、强大且跨平台的开发能力。1)C#是一种类型安全且面向对象的编程语言,旨在与.NET框架无缝集成。2).NET运行时管理C#代码的执行,提供垃圾回收、类型安全等服务,确保高效和跨平台运行。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具