C# 2.0 Specification (泛型三)

接泛型二
这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的。http://msdn.microsoft.com/vcsharp/team/language/default.aspx
20.4 泛型委托声明
委托声明可以包含类型参数。

delegate-declaration:

attributes opt delegate-modifiers op t delegate return-type identifier type-parameter-list opt

(formal-parameter-list opt) type-parameter-constraints-clauses opt;

(委托声明: 特性可选 委托修饰符可选 delegate 返回类型 标识符 类型参数列表可选 (正式参数列表可选 )类型参数约束语句可选

使用类型参数声明的委托是一个泛型委托声明。委托声明只有在支持类型参数列表时,才能支持类型参数约束语句(§20.7)。除了所指出的之外,泛型委托声明和常规的委托声明遵循相同的规则。泛型委托声明中的每个类型参数,在与委托关联的特定声明空间(§3.3)定义了一个名字。在委托声明中的类型参数的作用域包括返回类型、正式参数列表和类型参数约束语句。

像其他泛型类型声明一样,必须给定类型实参以形成构造委托类型。构造委托类型的参数和返回值,由委托声明中构造委托类型的每个类型参数对应的实参替代所形成。而结果返回类型和参数类型用于确定什么方法与构造委托类型兼容。例如

delegate bool Predicate<T>(T value)

class X

{

static bool F(int i){…}

static bool G(string s){…}

static void Main(){

Predicate<int> p1 = F;

Predicate<string> p2=G;

}

}

注意在先前的Main方法中的两个赋值等价于下面的较长形式.

static void Main(){

Predicate<int> p1 = new Predicate<int>(F);

Predicate<string> p2 = new Predicate<string>(G);

}

由于方法组转换,较短的形式也是可以的,这在§21.9中有说明。

20.5构造类型
泛型类型声明自身并不表示一个类型。相反,泛型类型声明通过应用类型实参的方式被用作形成许多不同类型的“蓝图”。类型参数被写在尖括号之间,并紧随泛型类型声明名字之后。使用至少一个实参而被命名的类型被称为构造类型(constructed type)。构造类型可以用在语言中类型名字可以出现的大多数地方。

type-name:(类型名字:)

namespace-or-type-name(命名空间或类型名字)

namespace-or-type-name:(命名空间或类型名字:)

identifier type-argument-list(标识符类型实参列表可选)

namespace-or-type-name. identifier(命名空间或类型名字.标识符)

type-argument-list opt(类型实参列表可选)

构造类型也能被用在表达式中作为简单名字(§20.9.3)或者访问一个成员(§20.9.4)。

当一个命名空间或类型名字被计算时,只有带有正确数量类型参数的泛型类型会被考虑。由此,只要类型有不同数量的类型参数并且声明在不同的命名空间,那么使用相同的标识符标识不同的类型是可能的。这对于在同一程序中混合使用泛型和非泛型类是很有用的。

namespace System.Collections

{

class Queue{…}

}

namespace Sysetm.Collections.Generic

{

class Queue<ElementType>{…}

}

namespace MyApplication

{

using System.Collections;

using System.Collections.Generic;

class X

{

Queue q1; //System.Collections.Queue

Queue<int> q2;//System.Collections.Generic.Queue

}

}

在这些代码中对于名字查找的详细规则在§20.9中进行了描述。在这些代码中对于模糊性的决议在§20.6.5中进行了描述。

类型名字可能标识一个构造类型,尽管它没有直接指定类型参数。这种情况在一个类型嵌套在一个泛型类声明中时就会出现,并且包含声明的实例类型将因为名字查找(§20.1.2)而被隐式地使用。

class Outer<T>

{

public class Inner{…}

public Inner i; //i的类型是Outer<T>.Inner

}

在不安全代码中,构造类型不能被用作非托管类型(§18.2)。

20.5.1类型实参
在一个类型参数列表中的每个实参都只是一个类型。

type-argument-list:(类型实参列表:)

<type-arguments>(<类型实参>)

type-arguments:(类型实参:)

type-argument(类型实参)

type-arguments, type-argument(类型实参,类型实参)

type-argument:(类型实参:)

type(类型)

类型实参反过来也可以是构造类型或类型参数。在不安全代码中(§18),类型实参不能是指针类型。每个类型实参必须遵循对应类型参数(§20.7.1)上的任何约束。

20.5.2开放和封闭类型
所有类型都可以被分为开放类型(open type)或封闭类型(closed type)。开放类型是包含类型参数的类型。更明确的说法是

类型参数定义了一个开放类型
数组类型只有当其元素是一个开放类型时才是开放类型
构造类型只有当其类型实参中的一个或多个是开放类型时,它才是开放类型

非开放类型都是封闭类型。

在运行时,在泛型类型声明中的所有代码都在一个封闭构造类型的上下文执行,这个封闭构造类型是通过将类型实参应用到泛型声明中创建的。在泛型类型中的每个类型实参被绑定到一个特定运行时类型。所有语句和表达式的运行时处理总是针对封闭类型发生,而开放类型只发生在编译时处理。

每个封闭构造类型都有它自己的一组静态变量,它们并不被其他封闭类型共享。因为在运行时不存在开放类型,所以开放类型没有关联的静态变量。如果两个封闭构造类型是从同一个类型声明构造的,并且对应的类型实参也是相同的类型,那么它们就是相同的类型。

20.5.3构造类型的基类和接口
构造类类型有一个直接基类,就像是一个简单类类型。如果泛型类声明没有指定基类,其基类为object。如果基类在泛型类声明中被指定,构造类型的基类通过将在基类声明中的每个类型参数,替代为构造类型对应类型实参而得到。给定泛型类声明

class B<U , V>{…}

class G<T>:B<string , T[]>{…}

构造类型G<int>的基类将会是B<string , int[]>。

相似地,构造类、结构和接口类型有一组显式的基接口。显式基接口通过接受泛型类型声明中的显式基接口声明和某种替代而形成,这种替代是将在基接口声明中的每个类型参数,替代为构造类型的对应类型实参。

一个类型的所有基类和基接口通过递归地得到中间基类和接口的基类与接口而形成。例如,给定泛型类声明

class A {…}

class B<T>:A{…}

class C<T>:B<IComparable<T>>{…}

class D<T>:C<T[]>{…}

D<int>的基类是C<int[]>,B<IComparable<int[]>>,A和object。

20.5.4构造类型的成员
构造类型的非继承成员通过替代成员声明的类型实参,构造类型的对应类型实参而得到。

例如,给定泛型类声明

class Gen<T,U>

{

public T[,],a;

public void G(int i ,T t , Gen<U, T> gt){…}

public U Prop(get{…}) set{…}}

public int H{double d}{…}

}

构造类型Gen<int[],IComparable<string>>有如下的成员。

public int[,][] a;

public void G(int I , int[] t , Gen<IComparable<string>,int[] gt>){…}

public IComparable<string> Prop{get{…} set{…}}

public int H(double d){…}

注意替代处理是基于类型声明的语义意义的,并不是简单的基于文本的替代。在泛型类声明Gen中的成员a的类型是“T的二维数组” 因此在先前实例化类型中的成员a的类型是“int型的一维数组的二维数组”或int[,][]。

构造类型的继承成员以一种相似的方法得到。首先直接基类的所有成员是已经确定的。如果基类自身是构造类型这可能包括当前规则的递归应用。然后,继承成员的每一个通过将成员声明中的每个类型参数,替代为构造类型对应类型实参而被转换。

class B<U>

{

public U F(long index){…}

}

class D<T>:B<T[]>

{

public T G(string s){…}

}

在先前的例子中,构造类型D<int>的非继承成员public int G(string s)通过替代类型参数T的类型实参int而得到。D<int>也有一个从类声明B而来的继承成员。这个继承成员通过首先确定构造类型B<T[]>的成员而被确定,B<T[]>成员的确定是通过将U替换为替换为T[],产生public T[] F(long index)。然后类型实参int替换了类型参数T,产生继承成员public int[] F(long index)。

20.5.5构造类型的可访问性
当构造类型C<T1,…,TN>的所有部分C,T1,…,TN 可访问时,那么它就是可访问的。例如,如果泛型类型名C是public,并且所有类型参数T1,…,TN也是public ,那么构造类型的可访问性也是public 。如果类型名或类型实参之一是private,那么构造类型的可访问性是private。如果类型实参之一可访问性是protected,另一个是internal,那么构造类型的可访问性仅限于该类,以及本程序集之内的子类。

20.5.6转换
构造类型遵循与非泛型类型相同的规则(§6)。当应用这些规则时,构造类型的基类和接口必须按§20.5.3中所描述的方式确定。

除了那些在§6中所描述的之外,构造引用类型之间不存在特别的转换。尤其是,不像数组类型,构造引用类型不允许“co-variant”转换。也就是说,类型List<B>不能转换到类型List<A>(无论是隐式或显式)即使是B派生于A也是如此。同样,也不存在从List<B>到List<object>的转换。

对于这一点的基本原理是很简单的:如果可以转换到List<A>,很显然你可以存储一个类型A的值到这个list中。这将破坏在List<B>类型中的每个对象总是类型B的值这种不变性,或者当在集合类上赋值时,将出现不可预料的错误。

转换的行为和运行时类型检查演示如下。

class A {…}

class B:A{…}

class Colletion{…}

class List<T>:Collection{…}

class Test

{

void F()

{

List<A> listA = new List<A>();

List<B> listB= new List<B>();

Collection c1 = listA; //OK,List<A>是一个集合

Collection c2 = listB; //OK,List<B>是一个集合

List<A> a1 = listB; //错误,没有隐式的转换

List<A> a2 = (List<A>)listB; //错误,没有显式的转换

}

}

20.5.7System.Nullable<T>类型
在.NET基类库中定义了泛型结构类型System.Nullable<T>泛型结构类型,它表示一个类型T的值可以为null。System.Nullable<T>类型在很多情形下是很有用的,例如用于指示数据库表的可空列,或者XML元素中的可选特性。

可以从一个null类型向任何由System.Nullable<T>类型构造的类型作隐式地转换。这种转换的结果就是System.Nullable<T>的默认值。也就是说,可以这样写

Nullable<int> x = null;

Nullable<string> y = null;

和下面的写法相同。

Nullable<int> x = Nullable<int>.default;

Nullable<string> y = Nullable<string>.default;

20.5.8使用别名指令
使用别名可以命名一个封闭构造类型,但不能命名一个没有提供类型实参的泛型类型声明。例如

namespace N1

{

class A<T>

{

class B{}

}

class C{}

}

namespace N2

{

using W = N1.A; //错误,不能命名泛型类型

using X = N1.A.B; //错误,不能命名泛型类型

using Y = N1.A<int>; //ok,可以命名封闭构造类型

using Z = N1.C; //ok

}

20.5.9特性
开放类型不能被用于特性内的任何地方。一个封闭构造类型可以被用作特性的实参,但不能被用作特性名,因为System.Attribute不可能是泛型类声明的基类。

class A:Attribute

{

public A(Type t){…}

}

class B<T>: Attribute{} //错误,不能将Attribute用作基类

class List<T>

{

[A(typeof(T))] T t; //错误,在特性中有开放类型

}

class X

{

[A(typeof(List<int>))] int x; //ok,封闭构造类型

[B<int>] int y; //错误,无效的特性名字

}

时间: 2025-01-27 18:51:20

C# 2.0 Specification (泛型三)的相关文章

C# 2.0 Specification (泛型四)

接泛型三这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的.http://msdn.microsoft.com/vcsharp/team/language/default.aspx20.6泛型方法泛型方法是与特定类型相关的方法.除了常规参数,泛型方法还命名了在使用方法时需要提供的一组类型参数.泛型方法可以在类.结构或接口声明中声明,而它们本身可以是泛型或者非泛型的.如果一个泛型方法在一个泛型类型声明中被声明,那么方法体可以引用方法的类型参数和包含声明的类型参数. class-

C#2.0 Specification(泛型一)

这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的.http://msdn.microsoft.com/vcsharp/team/language/default.aspx由于这一章非常长可能需要分几篇:)20.泛型20.1泛型类声明泛型类声明是一个需要提供类型参数以形成实际类型的类的声明. 类声明可以有选择地定义类型参数. class-declaration: (类声明) attributesopt class-modifiersopt class identifierop

C#2.0 Specification(泛型二)

接(泛型一)这篇文章是翻译的微软的技术文章.供学习c#的朋友参考,请勿用于商业目的.http://msdn.microsoft.com/vcsharp/team/language/default.aspx20.1.6泛型类中的静态构造函数在泛型类中的静态构造函数被用于初始化静态字段,为每个从特定泛型类声明中创建的不同的封闭构造类型,执行其他初始化.泛型类型声明的类型参数在作用域之内,可以在静态构造函数体内被使用. 如果下列情形之一发生,一个新的封闭构造类类型将被首次初始化. 一个封闭构造类型的实

C# 2.0 Specification(泛型五)

接泛型四20.6.5语法歧义在§20.9.3和§20.9.4中简单名字(simple-name)和成员访问(member-access)对于表达式来说容易引起语法歧义.例如,语句F(G<A,B>(7)); 可以被解释为对带有两个参数G<A和B>(7)的F的调用[1].同样,它还能被解释为对带有一个参数的F的调用,这是一个对带有两个类型实参和一个正式参数的泛型方法G的调用.如果表达式可以被解析为两种不同的有效方法,那么在">"能被解析作为运算符的所有或一部分

C#2.0的泛型代理和事件 :以一当百的快感

今天用C#2.0的泛型改写了我的一个系统的消息实现,感觉非常不错,很简洁而且速度快了(MSDN上那么说的,暗示诱导使我也有此感觉,呵呵). 我们唠会儿嗑,回顾一下经典内容:.NET框架的消息模型,构建于一个连接事件句柄的代理(delegate),要想触发某个事件,两个基本元素是必须考虑的: 1.事件的源头触发者,它定义了一个事件:public class EventSender{     ...     //声明一个代理类型,本例中,它指向一个无返回值的方法,并有两个参数     public

C# 2.0 Specification(迭代器)(二)

22.4 yield 语句yield语句用于迭代器块以产生一个枚举器对象值,或表明迭代的结束. embedded-statement:(嵌入语句)...yield-statement(yield语句) yield-statement:(yield 语句)yield return expression ;yield break ; 为了确保和现存程序的兼容性,yield并不是一个保留字,并且 yield只有在紧邻return或break关键词之前才具有特别的意义.而在其他上下文中,它可以被用作标识

黄聪:Microsoft Enterprise Library 5.0 系列教程(三) Validation Application Block (初级)

原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(三) Validation Application Block (初级) 企业库提供了一个很强大的验证应用程序模块,特点是: 可以通过配置为你的程序中特定的类来定义规则集. 是为你的类的公有属性,即对外开放的属性进行验证的. 使用企业库验证应用程序模块的优势: 有助于保持一致的验证方法. 包括大多数标准验证,包括.NET数据类型校验. 它让您可以将多个规则集具有相同的类和该类的成员. 它可以让你申请一个或多个

黄聪:Microsoft Enterprise Library 5.0 系列教程(三) Validation Application Block (高级)

原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(三) Validation Application Block (高级) 企业库验证应用程序模块之配置文件模式:   1.       新建一个控制台应用程序,并创建一个Customer类,其代码如下所示: 代码 using System;using System.Collections.Generic;using System.Linq;using System.Text; using Microsoft.

mfc opencv-MFC中使用opencv1.0同时打开三个摄像头时出错

问题描述 MFC中使用opencv1.0同时打开三个摄像头时出错 可以同时打开两个,但是当打开第三个的时候,就会发生错误,显示的错误信息(XP上)是:程序出错,需要进行调试.开始以为是三个摄像头的设备号(PID/VID)相同导致,采用的解决方案是在注册表HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlusbflags中添加,GlobalDisableSerNumGen=hex:01和IgnoreHWSerNum=hex:01两项,但是问题依然无法解