ホームページ  >  記事  >  バックエンド開発  >  C# 2.0 仕様 (ジェネリック 3)

C# 2.0 仕様 (ジェネリック 3)

黄舟
黄舟オリジナル
2017-01-03 10:32:211000ブラウズ

ジェネリック2からの続き

20.4 ジェネリックデリゲート宣言

デリゲート宣言には型パラメータを含めることができます。

delegate-declaration:
attributes opt delegate-modifiers op t デリゲート戻り型識別子 type-parameter-list opt
(formal-parameter-list opt) type-parameter-constraints-clauses opt;
(デリゲート宣言: 属性はデリゲート修飾子 オプション デリゲート戻り型識別子 型パラメーター リスト オプション (仮パラメーター リスト オプション) 型パラメーター制約ステートメント オプション
型パラメーターを使用して宣言されたデリゲートは、型パラメーター制約ステートメントのみをサポートします。 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 メソッドの 2 つの代入は、以下の長い形式と同等であることに注意してください。


20.5 構築型

ジェネリック型宣言は、それ自体が型を表すのではなく、さまざまな型を形成するための「設計図」として使用されます。型パラメーターは、ジェネリック型宣言名の直後に山かっこで囲まれて記述されます。 少なくとも 1 つの実パラメーターで名前が付けられた型は、言語の型名で使用できます。


static void Main(){
Predicate<int> p1 = new Predicate<int>(F);
Predicate<string> p2 = new Predicate<string>(G);
}

構築型は、単純な名前 (§20.9.3) として式内で使用することも、メンバーにアクセスするために (§20.9.4) も使用できます。

名前空間または型が評価されるときは、ジェネリックのみが評価されます。したがって、型パラメータの数が異なり、異なる名前空間で宣言されている限り、同じ識別子を使用して異なる型を識別することが適切です。同じプログラム内の汎用クラスと非汎用クラス

型パラメータが直接指定されていない場合でも、型名は構築された型を識別する場合があります。この状況は、型がジェネリック クラス宣言内でネストされており、名前検索 (§20.1.2) により、その宣言を含むインスタンス型が暗黙的に使用される場合に発生します。


type-name:(类型名字:)
namespace-or-type-name(命名空间或类型名字)
namespace-or-type-name:(命名空间或类型名字:)
identifier type-argument-list(标识符类型实参列表可选)
namespace-or-type-name. identifier(命名空间或类型名字.标识符)
type-argument-list opt(类型实参列表可选)

安全でないコードでは、構築された型をアンマネージ型として使用することはできません (§18.2)。

20.5.1 型引数


型パラメータリスト内の各引数は単なる型です。


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
}
}

型引数は、構築された型または型パラメーターにすることができます。安全でないコード (§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[]>{…}

构造类型Gbd43222e33876353aff11e13a7dc75f6的基类将会是Bfef199af9bef46a927b6ef6994413e6c。

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



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

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}{…}
}

构造类型Gen524584304588ce96ab320bc282e4889e>有如下的成员。

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){…}
}

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

20.5.5构造类型的可访问性

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

20.5.6转换

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

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

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

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

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

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 までご連絡ください。