C# 2.0 Specification(泛型五)
来源:互联网 发布:js如何重新加载页面 编辑:程序博客网 时间:2024/05/20 11:25
<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
可以被解释为对带有两个参数G<A和B>(7)的F的调用[1]。同样,它还能被解释为对带有一个参数的F的调用,这是一个对带有两个类型实参和一个正式参数的泛型方法G的调用。如果表达式可以被解析为两种不同的有效方法,那么在“>”能被解析作为运算符的所有或一部分时,或者作为一个类型实参列表,那么紧随“>”之后的标记将会被检查。如果它是如下之一:{ } ] > : ; , . ?那么“>”被解析作为类型实参列表。否则“>”被解析作为一个运算符。
在where语句中给定的约束列表可以以这个顺序包含下列组件:一个单一的类约束、一个或多个接口约和构造函数约束new ()。如果约束是一个类类型或者接口类型,这个类型指定类型参数必须支持的每个类型实参的最小“基类型”。无论什么时候使用一个构造类型或者泛型方法,在编译时对于类型实参的约束建会被检查。所提供的类型实参必须派生于或者实现那个类型参数个定的所有约束。被指定作为类约束的类型必须遵循下面的规则。
interface IComparable<T>{ int CompareTo(T value);}interface IKeyProvider<T>{ T GetKey();}class Printer<T> where T:IPrintable{…}class SortedList<T> where T: IComparable<T>{…}class Dictionary<K,V> where K:IComparable<K> where: V: IPrintable,IKeyProvider<K>,new(){ …} 下面的例子是一个错误,因为它试图直接使用一个类型参数作为约束。class Extend<T , U> where U:T{…}//错误约束的类型参数类型的值可以被用于访问约束暗示的实例成员。在例子interface IPrintable{ void Print();}class Printer<T> where T:IPrintable{ void PrintOne(T x){x.Pint();}}IPrintable的方法可以在x上被直接调用,因为T被约束总是实现IPrintable。
在泛型出现之前,成员查找总是返回在类中唯一声明的一组成员,或者一组在接口中唯一声明的成员, 也可能是object类型。在类型参数上的成员查找做出了一些改变。当一个类型参数有一个类约束和一个或多个接口约束时,成员查找可以返回一组成员,这些成员有一些是在类中声明的,还有一些是在接口中声明的。下面的附加规则处理了这种情况。
程序的输出如下123尽管推荐不要让ToString带有附加效果(side effect)[2],但这个例子说明了对于三次x.ToString()的调用不会发生装箱。当在一个约束的类型参数上访问一个成员时,装箱决不会隐式地发生。例如,假定一个接口ICounter包含了一个方法Increment,它可以被用来修改一个值。如果ICounter被用作一个约束,Increment方法的实现将通过Increment在其上调用的变量的引用而被调用,这个变量不是一个装箱拷贝。using System;interface ICounter{ void Increment();}struct Counter:ICounter{ int value; public override string ToString() { return value.ToString(); }void ICounter.Increment(){value++;}} class Program{ static void Test<T>() where T:new ,ICounter{T x = new T();Console.WriteLine(x);x.Increment(); //修改x`Console.WriteLine(x); ((ICounter)x).Increment(); //修改x的装箱拷贝Console.WriteLine(x);}static void Main(){ Test<Counter>();}}
对变量x的首次调用Increment修改了它的值。这与第二次调用Increment是不等价的,第二次修改的是x装箱后的拷贝,因此程序的输出如下
[1] 这种情况下“>”被解释为大于运算符。[2] 在程序中重写ToString时,一般不推荐添加这种类似的计算逻辑,因为它的这种结果变化不易控制,增加了调试程序的复杂性。[3] C的基类或其基类的基类等。<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 160x600, 创建于 08-4-23MSDN */google_ad_slot = "4367022601";google_ad_width = 160;google_ad_height = 600;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
接泛型四
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的调用。如果表达式可以被解析为两种不同的有效方法,那么在“>”能被解析作为运算符的所有或一部分时,或者作为一个类型实参列表,那么紧随“>”之后的标记将会被检查。如果它是如下之一:{ } ] > : ; , . ?那么“>”被解析作为类型实参列表。否则“>”被解析作为一个运算符。
20.6.6对委托使用泛型方法
委托的实例可通过引用一个泛型方法的声明而创建。委托表达式确切的编译时处理,包括引用泛型方法的委托创建表达式,这在§20.9.6中进行了描述。当通过委托调用一个泛型方法时,所使用的类型实参将在委托实例化时被确定。类型实参可以通过类型实参列表显式给定,或者通过类型推断(§20.6.4)而确定。如果采用类型推断,委托的参数类型将被用作推断处理过程的实参类型。委托的返回类型不用于推断。下面的例子展示了为一个委托实例化表达式提供类型实参的方法。delegate int D(string s , int i)delegate int E();class X{ public static T F<T>(string s ,T t){…} public static T G<T>(){…} static void Main(){D d1 = new D(F<int>); //ok,类型实参被显式给定D d2 = new D(F); //ok,int作为类型实参而被推断E e1 = new E(G<int>); //ok,类型实参被显式给定E e2 = new E(G); //错误,不能从返回类型推断}}在先前的例子中,非泛型委托类型使用泛型方法实例化。你也可以使用泛型方法创建一个构造委托类型的实例。在所有情形下,当委托实例被创建时,类型实参被给定或可以被推断,但委托被调用时,可以不用提供类型实参列表(§15.3)。20.6.7非泛型属性、事件、索引器或运算符
属性、事件、索引器和运算符他们自身可以没有类型实参(尽管他们可以出现在泛型类中,并且可从一个封闭类中使用类型实参)。如果需要一个类似属性泛型的构件,取而代之的是你必须使用一个泛型方法。20.7约束
泛型类型和方法声明可以可选的指定类型参数约束,这通过在声明中包含类型参数约束语句就可以做到。type-parameter-constraints-clauses(类型参数约束语句:) type-parameter-constraints-clause(类型参数约束语句) type-parameter-constraints-clauses type-parameter-constraints-clause(类型参数约束语句 类型参数约束语句)type-parameter-constraints-clause:(类型参数约束语句:) where type-parameter : type-parameter-constraints(where 类型参数:类型参数约束)type-parameter-constraints:(类型参数约束:) class-constraint(类约束) interface-constraints(接口约束) constructor-constraint(构造函数约束) class-constraint , interface-constraints(类约束,接口约束) class-constraint , constructor-constraint(类约束,构造函数约束) interface-constraints , constructor-constraint(接口约束,构造函数约束) class-constraint , interface-constraints , constructor-constraint(类约束,接口约束,构造函数约束)class-constraint:(类约束:) class-type(类类型)interface-constraint:(接口约束:) interface-constraint(接口约束) interface-constraints , interface-constraints(接口约束,接口约束)interface-constraints:(接口约束:) interface-type(接口类型)constructor-constraint:(构造函数约束:) new ( )每个类型参数约束语句由标志where 紧接着类型参数的名字,紧接着冒号和类型参数的约束列表。对每个类型参数只能有一个where 语句,但where语句可以以任何顺序列出。与属性访问器中的get和set标志相似,where 语句不是关键字。在where语句中给定的约束列表可以以这个顺序包含下列组件:一个单一的类约束、一个或多个接口约和构造函数约束new ()。如果约束是一个类类型或者接口类型,这个类型指定类型参数必须支持的每个类型实参的最小“基类型”。无论什么时候使用一个构造类型或者泛型方法,在编译时对于类型实参的约束建会被检查。所提供的类型实参必须派生于或者实现那个类型参数个定的所有约束。被指定作为类约束的类型必须遵循下面的规则。
- 该类型必须是一个类类型。
- 该类型必须是密封的(sealed)。
- 该类型不能是如下的类型:System.Array,System.Delegate,System.Enum,或者System.ValueType类型。
- 该类型不能是object。由于所有类型派生于object,如果容许的话这种约束将不会有什么作用。
- 至多,对于给定类型参数的约束可以是一个类类型。
- 该类型必须是一个接口类型。
- 在一个给定的where语句中相同的类型不能被指定多次。
interface IComparable<T>{ int CompareTo(T value);}interface IKeyProvider<T>{ T GetKey();}class Printer<T> where T:IPrintable{…}class SortedList<T> where T: IComparable<T>{…}class Dictionary<K,V> where K:IComparable<K> where: V: IPrintable,IKeyProvider<K>,new(){ …} 下面的例子是一个错误,因为它试图直接使用一个类型参数作为约束。class Extend<T , U> where U:T{…}//错误约束的类型参数类型的值可以被用于访问约束暗示的实例成员。在例子interface IPrintable{ void Print();}class Printer<T> where T:IPrintable{ void PrintOne(T x){x.Pint();}}IPrintable的方法可以在x上被直接调用,因为T被约束总是实现IPrintable。
20.7.1遵循约束
无论什么时候使用构造类型或者引用泛型方法,所提供的类型实参都将针对声明在泛型类型,或者方法中的类型参数约束作出检查。对于每个where 语句,对应于命名的类型参数的类型实参A将按如下每个约束作出检查。- 如果约束是一个类类型或者接口类型,让C表示提供类型实参的约束,该类型实参将替代出现在约束中的任何类型参数。为了遵循约束,类型A必须按如下方式可别转化为类型C:
- 如果约束是new(),类型实参A不能是abstract并,且必须有一个公有的无参的构造函数。如果如下之一是真实的这将可以得到满足。
20.7.2 在类型参数上的成员查找
在由类型参数T给定的类型中,成员查找的结果取决于为T所指定的约束(如果有的话)。如果T没有约束或者只有new ()约束,在T上的成员查找,像在object上的成员查找一样,返回一组相同的成员。否则,成员查找的第一个阶段,将考虑T所约束的每个类型的所有成员,结果将会被合并,然后隐藏成员将会从合并结果中删除。在泛型出现之前,成员查找总是返回在类中唯一声明的一组成员,或者一组在接口中唯一声明的成员, 也可能是object类型。在类型参数上的成员查找做出了一些改变。当一个类型参数有一个类约束和一个或多个接口约束时,成员查找可以返回一组成员,这些成员有一些是在类中声明的,还有一些是在接口中声明的。下面的附加规则处理了这种情况。
- 在成员查找过程(§20.9.2)中,在除了object之外的类中声明的成员隐藏了在接口中声明的成员。
- 在方法和索引器的重载决策过程中,如果任何可用成员在一个不同于object的类中声明,那么在接口中声明的所有成员都将从被考虑的成员集合中删除。
20.7.3 类型参数和装箱
当一个结构类型重写继承于System.Object(Equals , GetHashCode或ToString)的虚拟方法,通过结构类型的实例调用虚拟方法将不会导致装箱。即使当结构被用作一个类型参数,并且调用通过类型参数类型的实例而发生,情况也是如此。例如using System;struct Counter{ int value; public override string ToString(){ value++; return value.ToString();}}class Program{ static void Test<T>() where T:new(){T x = new T();Console.WriteLine(x.ToString());Console.WriteLine(x.ToString());Console.WriteLine(x.ToString());}static void Main(){Test<Counter>();}}程序的输出如下123尽管推荐不要让ToString带有附加效果(side effect)[2],但这个例子说明了对于三次x.ToString()的调用不会发生装箱。当在一个约束的类型参数上访问一个成员时,装箱决不会隐式地发生。例如,假定一个接口ICounter包含了一个方法Increment,它可以被用来修改一个值。如果ICounter被用作一个约束,Increment方法的实现将通过Increment在其上调用的变量的引用而被调用,这个变量不是一个装箱拷贝。using System;interface ICounter{ void Increment();}struct Counter:ICounter{ int value; public override string ToString() { return value.ToString(); }void ICounter.Increment(){value++;}} class Program{ static void Test<T>() where T:new ,ICounter{T x = new T();Console.WriteLine(x);x.Increment(); //修改x`Console.WriteLine(x); ((ICounter)x).Increment(); //修改x的装箱拷贝Console.WriteLine(x);}static void Main(){ Test<Counter>();}}
对变量x的首次调用Increment修改了它的值。这与第二次调用Increment是不等价的,第二次修改的是x装箱后的拷贝,因此程序的输出如下
20.7.4包含类型参数的转换
在类型参数T上允许的转换,取决于为T所指定的约束。所有约束的或非约束的类型参数,都可以有如下转换。- 从T到T的隐式同一转换。
- 从T到object 的隐式转换。在运行时,如果T是一个值类型,这将通过一个装箱转换进行。否则,它将作为一个隐式地引用转换。
- 从object到T的隐式转换。在运行时,如果T是一个值类型,这将通过一个取消装箱操作而进行。否则它将作为一个显式地引用转换。
- 从T到任何接口类型的显式转换。在运行时,如果T是一个值类型,这将通过一个装箱转换而进行。否则,它将通过一个显式地引用转换而进行。
- 从任何接口类型到T的隐式转换。在运行时,如果T是一个值类型,这将通过一个取消装箱操作而进行。否则,它将作为一个显式引用转换而进行。
- 从T到I的隐式转换,以及从T到I的任何基接口类型的转换。在运行时,如果T是一个值类型,这将作为一个装箱转换而进行。否则,它将作为一个隐式地引用转换而进行。
- 从T到C的隐式引用转换,从T到任何C从中派生的类,以及从T到任何从其实现的接口。
- 从C到T的显式引用转换,从C从中派生的类[3]到T,以及C实现的任何接口到T
- 如果存在从C 到A的隐式用户定义转换,从T到 A的隐式用户定义转换。
- 如果存在从A 到C的显式用户定义转换,从A到 T的显式用户定义转换。
- 从null类型到T的隐式引用转换
- 从带有元素类型T的数组类型AT到带有元素类型U的数组类型AU的隐式引用转换,并且如果下列二者成立的话,将存在从AU到AT显式引用转换:
[1] 这种情况下“>”被解释为大于运算符。[2] 在程序中重写ToString时,一般不推荐添加这种类似的计算逻辑,因为它的这种结果变化不易控制,增加了调试程序的复杂性。[3] C的基类或其基类的基类等。<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 160x600, 创建于 08-4-23MSDN */google_ad_slot = "4367022601";google_ad_width = 160;google_ad_height = 600;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
- C# 2.0 Specification(泛型五)
- C# 2.0 Specification(泛型五)
- C# 2.0 Specification(泛型五)
- C# 2.0 Specification(二)
- C# 2.0 Specification (四)
- C#2.0 Specification(泛型一)
- C#2.0 Specification(泛型二)
- C# 2.0 Specification (泛型三)
- C# 2.0 Specification (泛型四)
- C# 2.0 Specification(泛型六)
- C# Version 2.0 Specification
- C# 2.0 Specification (泛型三)
- C# 2.0 Specification (泛型四)
- C# 2.0 Specification(泛型六)
- C# 2.0 Specification(二)
- C# 2.0 Specification(泛型六)
- C# 2.0 Specification (四)
- C# 2.0 Specification(二)
- 在vb.net中实现窗体上回车键代替TAB键
- problem 2922
- 在.NET 应用程序中用System.Web.Mail 发送电子邮件
- 用 (*it).m 还是 it->m
- 通过避免下列 10 个常见 ASP.NET 缺陷使网站平稳运行
- C# 2.0 Specification(泛型五)
- [转载]ASP.NET AJAX入门系列
- 串口发送数据类
- C# 2.0 Specification(泛型六)
- 串口发送数据类
- 不用第归算法快速显示树,对于Oracle数据库(2)
- 简介使用ASP.NET访问Oracle数据库的方法
- ASP.NET Whidbey 中新的代码编译功能
- 使用更精简的代码保证 ASP.NET 应用程序的安全