Heim  >  Artikel  >  Backend-Entwicklung  >  C# 2.0-Spezifikation (Generics 3)

C# 2.0-Spezifikation (Generics 3)

黄舟
黄舟Original
2017-01-03 10:32:211000Durchsuche

Fortsetzung von Generics 2

20.4 Generische Delegiertendeklaration

Eine Delegiertendeklaration kann Typparameter enthalten.

Delegate-Declaration:
Attribute opt Delegat-Modifikatoren op t Delegat Rückgabetyp-ID Typ-Parameter-Liste opt
(formale-Parameter-Liste opt) Typ-Parameter-Einschränkungen-Klauseln opt ;
(Delegationsdeklaration: Attribute optionaler Delegate-Modifikator optionaler Delegate-Rückgabetypbezeichner Typparameterliste optional (formale Parameterliste optional) Typparameter-Einschränkungsanweisung optional
Ein mithilfe von Typparametern deklarierter Delegat ist eine generische Delegate-Deklaration. Eine Delegate-Deklaration kann Typparameter-Einschränkungsanweisungen (§20.7) nur unterstützen, wenn sie eine Typparameterliste unterstützen. Sofern nicht anders angegeben, folgen generische Delegatendeklarationen denselben Regeln wie reguläre Delegatendeklarationen. Jeder Typparameter in einer Deklaration hat einen Namen, der im spezifischen Deklarationsraum definiert ist (§3.3) Der Umfang eines Typparameters in einer Delegatendeklaration umfasst den Rückgabetyp, die formale Parameterliste und die Typparameter-Einschränkungsanweisung

Typargumente Die Parameter und Rückgabewerte des konstruierten Delegatentyps müssen durch die tatsächlichen Parameter ersetzt werden, die jedem Typparameter des konstruierten Delegatentyps in der Delegatendeklaration entsprechen werden verwendet, um zu bestimmen, welche Methode mit dem konstruierten Delegatentyp kompatibel ist.

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

Beachten Sie, dass die beiden Zuweisungen in der vorherigen Main-Methode der längeren Form unten entsprechen .

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

Die kürzere Form ist auch aufgrund der Methodengruppenkonvertierung möglich, die in §21.9 erläutert wird. 🎜>20.5 Konstruierte Typen

Eine generische Typdeklaration stellt selbst keinen Typ dar. Stattdessen wird eine generische Typdeklaration als „Blaupause“ für die Bildung vieler verschiedener Typen verwendet, die in spitzen Klammern steht und unmittelbar auf den Namen der generischen Typdeklaration folgt Mindestens ein Argument wird als konstruierter Typ bezeichnet, wenn Typnamen in der Sprache vorkommen können.



Konstruierte Typen können auch in Ausdrücken verwendet werden >

Wenn ein Namespace oder Typname ausgewertet wird, werden nur generische Typen mit der richtigen Anzahl von Typparametern berücksichtigt Solange der Typ eine unterschiedliche Anzahl von Typparametern hat und in einem anderen Namespace deklariert ist, ist es möglich, denselben Bezeichner zu verwenden, um verschiedene Typen zu identifizieren. Dies ist nützlich, um generische und nicht generische Klassen im selben Programm zu verwenden Detaillierte Regeln für die Namenssuche im Code sind in §20.9 beschrieben. Die Lösung von Mehrdeutigkeiten in diesen Codes wird in §20.6.5 beschrieben.
type-name:(类型名字:)
namespace-or-type-name(命名空间或类型名字)
namespace-or-type-name:(命名空间或类型名字:)
identifier type-argument-list(标识符类型实参列表可选)
namespace-or-type-name. identifier(命名空间或类型名字.标识符)
type-argument-list opt(类型实参列表可选)

Ein Typname kann einen konstruierten Typ identifizieren, auch wenn er Typparameter nicht direkt angibt. Diese Situation tritt auf, wenn ein Typ in einer generischen Klassendeklaration verschachtelt ist und der Instanztyp der enthaltenden Deklaration aufgrund der Namenssuche implizit verwendet wird (§20.1.2).



Konstruierte Typen können nicht als nicht verwaltete Typen in unsicherem Code verwendet werden (§18.2).
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.5.1 Typargumente

Jeder tatsächliche Parameter in einer Typparameterliste ist nur ein Typ.

class Outer<T>
{
public class Inner{…}
public Inner i; //i的类型是Outer<T>.Inner
}


Typargumente können wiederum konstruierte Typen oder Typparameter sein. In unsicherem Code (§18) können Typargumente keine Zeigertypen sein. Jedes Typargument muss allen Einschränkungen für den entsprechenden Typparameter entsprechen (§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)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn