首页 后端开发 C#.Net教程 C# 2.0 Specification (泛型三)

C# 2.0 Specification (泛型三)

Jan 03, 2017 am 10:32 AM

接泛型二

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的基类将会是B

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



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

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>有如下的成员。

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

20.5.5构造类型的可访问性

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

20.5.6转换

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

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

对于这一点的基本原理是很简单的:如果可以转换到List,很显然你可以存储一个类型A的值到这个list中。这将破坏在List类型中的每个对象总是类型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类型

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

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

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; //错误,无效的特性名字
}
登录后复制

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


本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热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

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

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

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

char在C语言字符串中的作用是什么 char在C语言字符串中的作用是什么 Apr 03, 2025 pm 03:15 PM

在 C 语言中,char 类型在字符串中用于:1. 存储单个字符;2. 使用数组表示字符串并以 null 终止符结束;3. 通过字符串操作函数进行操作;4. 从键盘读取或输出字符串。

C语言各种符号的使用方法 C语言各种符号的使用方法 Apr 03, 2025 pm 04:48 PM

C 语言中符号的使用方法涵盖算术、赋值、条件、逻辑、位运算符等。算术运算符用于基本数学运算,赋值运算符用于赋值和加减乘除赋值,条件运算符用于根据条件执行不同操作,逻辑运算符用于逻辑操作,位运算符用于位级操作,特殊常量用于表示空指针、文件结束标记和非数字值。

char在C语言中如何处理特殊字符 char在C语言中如何处理特殊字符 Apr 03, 2025 pm 03:18 PM

C语言中通过转义序列处理特殊字符,如:\n表示换行符。\t表示制表符。使用转义序列或字符常量表示特殊字符,如char c = '\n'。注意,反斜杠需要转义两次。不同平台和编译器可能有不同的转义序列,请查阅文档。

c#多线程和异步的区别 c#多线程和异步的区别 Apr 03, 2025 pm 02:57 PM

多线程和异步的区别在于,多线程同时执行多个线程,而异步在不阻塞当前线程的情况下执行操作。多线程用于计算密集型任务,而异步用于用户交互操作。多线程的优势是提高计算性能,异步的优势是不阻塞 UI 线程。选择多线程还是异步取决于任务性质:计算密集型任务使用多线程,与外部资源交互且需要保持 UI 响应的任务使用异步。

char与wchar_t在C语言中的区别 char与wchar_t在C语言中的区别 Apr 03, 2025 pm 03:09 PM

在 C 语言中,char 和 wchar_t 的主要区别在于字符编码:char 使用 ASCII 或扩展 ASCII,wchar_t 使用 Unicode;char 占用 1-2 个字节,wchar_t 占用 2-4 个字节;char 适用于英语文本,wchar_t 适用于多语言文本;char 广泛支持,wchar_t 依赖于编译器和操作系统是否支持 Unicode;char 的字符范围受限,wchar_t 的字符范围更大,并使用专门的函数进行算术运算。

char在C语言中如何进行类型转换 char在C语言中如何进行类型转换 Apr 03, 2025 pm 03:21 PM

在 C 语言中,char 类型转换可以通过:强制类型转换:使用强制类型转换符将一种类型的数据直接转换为另一种类型。自动类型转换:当一种类型的数据可以容纳另一种类型的值时,编译器自动进行转换。

char数组在C语言中如何使用 char数组在C语言中如何使用 Apr 03, 2025 pm 03:24 PM

char 数组在 C 语言中存储字符序列,声明为 char array_name[size]。访问元素通过下标运算符,元素以空终止符 '\0' 结尾,用于表示字符串终点。C 语言提供多种字符串操作函数,如 strlen()、strcpy()、strcat() 和 strcmp()。

C语言 sum 的作用是什么? C语言 sum 的作用是什么? Apr 03, 2025 pm 02:21 PM

C语言中没有内置求和函数,需自行编写。可通过遍历数组并累加元素实现求和:循环版本:使用for循环和数组长度计算求和。指针版本:使用指针指向数组元素,通过自增指针遍历高效求和。动态分配数组版本:动态分配数组并自行管理内存,确保释放已分配内存以防止内存泄漏。

See all articles