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

Jan 03, 2017 am 10:27 AM

20.1.6 ジェネリッククラスの静的コンストラクター

ジェネリッククラスの静的コンストラクターは、静的フィールドの初期化やその他の初期化を実行するために使用されます。ジェネリック型によって宣言された型パラメーターはスコープ内にあり、静的コンストラクターの本体内で使用できます。

次のいずれかの状況が発生した場合、新しい閉じられた構築クラス型が初めて初期化されます。

閉じた構築型のインスタンスが作成されたとき

閉じた構築型の静的メンバーが参照されたとき

新しい閉じた構築クラス型を初期化するには、まず、その特定の閉じた型の静的フィールドの新しいセット (§ 20.1 .5)が作成されます。各静的フィールドはデフォルト値 (§5.2) に初期化されます。次に、これらの静的フィールドに対して静的フィールド初期化子 (§10.4.5.1) が実行されます。最後に静的コンストラクターが実行されます。

静的コンストラクターは、それを囲んでいる構築されたクラス型ごとに 1 回実行されるため、制約 (§20.7) によってチェックできない型パラメーターに対して実行時チェックを実装すると便利です。たとえば、次の型は静的コンストラクターを使用して、型パラメーターが参照型であるかどうかを確認します。

class Gen<T>
{
static Gen(){
if((object)T.default != null){
throw new ArgumentException(“T must be a reference type”);
}
}
}
ログイン後にコピー

20.1.7 保護されたメンバーへのアクセス

ジェネリック クラス宣言では、ジェネリック クラスから構築された任意の型のインスタンスを通じて、継承された保護されたインスタンス メンバーへのアクセスが許可されます。特に、§3.5.3 で指定されている保護された内部インスタンス メンバーにアクセスするための規則は、次の規則を使用してジェネリック用に拡張されます。

ジェネリック クラス G では、継承された保護インスタンス メンバー M に対して、E の型が G から構築されたクラス型であるか、構築されたクラス型のクラス型である場合に限り、E.M を使用した基本式が許可されます。 G 著



class C<T>
{
protected T x;
}
class D<T> :C<T>
{
static void F(){
D<T> dt = new D<T>();
D<int> di = new D<int>();
D<string> ds = new D<string>();
dt.x = T.default;
di.x = 123;
ds.x = “test”;
}
}
ログイン後にコピー

では、x への 3 つの代入ステートメントが許可されています。これらはすべて、ジェネリックから構築されたクラス型のインスタンスを通じて発生するためです。

20.1.8 ジェネリッククラスでのオーバーロード

ジェネリッククラス宣言内のメソッド、コンストラクター、インデクサー、演算子はオーバーロードできます。ただし、構築されたクラスのあいまいさを避けるために、これらのオーバーロードは制限されています。同じジェネリック クラス宣言内で同じ名前で宣言された 2 つの関数メンバーには、そのようなパラメーター型が必要です。つまり、同じ名前とシグネチャを持つ 2 つのメンバーを閉じた構築型に含めることはできません。考えられるすべての閉じられた構築型を考慮すると、この規則には現在のプログラムに現在存在しない型が引数である可能性が含まれますが、それでも可能性はあります [1]。このルールでは、型パラメーターの型制約は無視されます。

以下の例は、このルールに従って有効なオーバーロードと無効なオーバーロードを示しています。

nterface I1<T> {…}
interface I2<T>{…}

class G1<U>
{
long F1(U u); //无效重载,G<int>将会有使用相同签名的两个成员
int F1(int i);
void F2(U u1, U u2); //有效重载,对于U没有类型参数
void F2(int I , string s); //可能同时是int和string 
void F3(I1<U>a); //有效重载
void F3(I2<U>a);
void F4(U a); //有效重载
void F4(U[] a);}

class G2<U,V>
{
void F5(U u , V v); //无效重载,G2<int , int>将会有两个签名相同的成员
void F5(V v, U u);
void F6(U u , I1<V> v);//无效重载,G2<I1<int>,int>将会有两个签名相同的成员
void F6(I1<V> v , U u);
void F7(U u1,I1<V> V2);//有效的重载,U不可能同时是V和I1<V>
void F7(V v1 , U u2);
void F8(ref U u); //无效重载
void F8(out V v);
}
class C1{…}
class C2{…}
class G3<U , V> where U:C1 where V:C2
{
void F9(U u); //无效重载,当检查重载时,在U和V上的约束将被忽略
void F9(V v);
}
ログイン後にコピー

0.1.9 パラメータ配列メソッドと型パラメータ

パラメータ配列の型には型パラメータが使用できます。たとえば、宣言

class C<V>
{
static void F(int x, int y ,params V[] args);
}
方法的扩展形式的如下调用

C<int>.F(10, 20);
C<object>.F(10,20,30,40);
C<string>.F(10,20,”hello”,”goodbye”);

对应于如下形式:

C<int>.F(10,20, new int[]{});
C<object>.F(10,20,new object[]{30,40});
C<string>.F(10,20,new string[](“hello”,”goodbye”));
ログイン後にコピー

20.1.10 オーバーライドとジェネリック クラス

があるとします。

在泛型类中的函数成员可以重写基类中的函数成员。如果基类是一个非泛型类型或封闭构造类型,那么任何重写函数成员不能有包含类型参数的组成类型。然而,如果一个基类是一个开放构造类型,那么重写函数成员可以使用在其声明中的类型参数。当重写基类成员时,基类成员必须通过替换类型实参而被确定,如§20.5.4中所描述的。一旦基类的成员被确定,用于重写的规则和非泛型类是一样的。

下面的例子演示了对于现有的泛型其重写规则是如何工作的。

abstract class C<T>
{
public virtual T F(){…}
public virtual C<T> G(){…}
public virtual void H(C<T> x ){…}
} 
class D:C<string>
{
public override string F(){…}//OK
public override C<string> G(){…}//OK
public override void H(C<T> x); //错误,应该是C<string>
}
class E<T,U>:C<U>
{
public override U F(){…}//OK
public override C<U> G(){…}//OK
public override void H(C<T> x){…}//错误,应该是C<U>
}
ログイン後にコピー

20.1.11泛型类中的运算符

泛型类声明可以定义运算符,它遵循和常规类相同的规则。类声明的实例类型(§20.1.2)必须以一种类似于运算符的常规规则的方式,在运算符声明中被使用,如下

一元运算符必须接受一个实例类型的单一参数。一元运算符“++”和“—”必须返回实例类型。

至少二元运算符的参数之一必须是实例类型。

转换运算符的参数类型和返回类型都必须是实例类型。


下面展示了在泛型类中几个有效的运算符声明的例子

class X<T>
{
public static X<T> operator ++(X(T) operand){…}
public static int operator *(X<T> op1, int op2){…}
public static explicit operator X<T>(T value){…}
}
ログイン後にコピー

对于一个从源类型S到目标类型T的转换运算符,当应用§10.9.3中的规则时,任何关联S或T的类型参数被认为是唯一类型,它们与其他类型没有继承关系,并且在这些类型参数上的任何约束都将被忽略。

在例子

class C<T>{…}
class D<T>:C<T>
{
public static implicit operator C<int>(D<T> value){…}//OK
public static implicit operator C<T>(D<T> value){…}//错误
}
ログイン後にコピー

第一个运算符声明是允许的,由于§10.9.3的原因,T和int被认为是没有关系的唯一类型。然而,第二个运算符是一个错误,因为C是D的基类。

给定先前的例子,为某些类型实参声明运算符,指定已经作为预定义转换而存在的转换是可能的。

struct Nullable<T>
{
public static implicit operator Nullable<T>(T value){…}
public static explicit operator T(Nullable<T> value){…}
}
ログイン後にコピー

当类型object作为T的类型实参被指定,第二个运算符声明了一个已经存在的转换(从任何类型到object是一个隐式的,也可以是显式的转换)。

在两个类型之间存在预定义的转换的情形下,在这些类型上的任何用户定义的转换都将被忽略。尤其是

如果存在从类型S到类型T的预定义的隐式转换(§6.1),所有用户定义的转换(隐式的或显式的)都将被忽略。

如果存在从类型S到类型T的预定义的显式转换,那么任何用户定义的从类型S到类型T的显式转换都将被忽略。但用户定义的从S到T的隐式转换仍会被考虑。

对于所有类型除了object,由Nullable类型声明的运算符都不会与预定义的转换冲突。例如

void F(int I , Nullable<int> n){
i = n; //错误
i = (int)n; //用户定义的显式转换
n = i; //用户定义的隐式转换
n = (Nullable<int>)i; //用户定义的隐式转换
}
ログイン後にコピー

然而,对于类型object,预定义的转换在所有情况隐藏了用户定义转换,除了一种情况:

void F(object o , Nullable<object> n){
o = n; //预定义装箱转换
o= (object)n; //预定义装箱转换
n= o; //用户定义隐式转换
n = (Nullable<object>)o; //预定义取消装箱转换
}
ログイン後にコピー


20.1.12泛型类中的嵌套类型

泛型类声明可以包含嵌套类型声明。封闭类的类型参数可以在嵌套类型中使用。嵌套类型声明可以包含附加的类型参数,它只适用于该嵌套类型。

包含在泛型类声明中的每个类型声明是隐式的泛型类型声明。当编写一个嵌套在泛型类型内的类型的引用时,包含构造类型,包括它的类型实参,必须被命名。然而,在外部类中,内部类型可以被无限制的使用;当构造一个内部类型时,外部类的实例类型可以被隐式地使用。下面的例子展示了三个不同的引用从Inner创建的构造类型的方法,它们都是正确的;前两个是等价的。
class Outer<T>
{
class Inner<U>
{
static void F(T t , U u){…}
}
static void F(T t)
{
Outer<T>.Inner<string >.F(t,”abc”);//这两个语句有同样的效果
Inner<string>.F(t,”abc”);
Outer<int>.Inner<string>.F(3,”abc”); //这个类型是不同的
Outer.Inner<string>.F(t , “abc”); //错误,Outer需要类型参数
}
}
ログイン後にコピー

尽管这是一种不好的编程风格,但嵌套类型中的类型参数可以隐藏一个成员,或在外部类型中声明的一个类型参数。

class Outer<T>
{
class Inner<T> //有效,隐藏了 Ouer的 T
{
public T t; //引用Inner的T
}
}
ログイン後にコピー

20.1.13应用程序入口点

应用程序入口点不能在一个泛型类声明中。

20.2泛型结构声明

像类声明一样,结构声明可以有可选的类型参数。

struct-declaration:(结构声明:)
attributes opt struct-modifiers opt struct identifier type-parameter-list opt struct-interfaces opt type-parameter-constraints-clauses opt struct-body ;opt
(特性可选 结构修饰符可选 struct 标识符 类型参数列表可选 结构接口可选 类型参数约束语句可选 结构体;可选)
ログイン後にコピー

除了§11.3中为结构声明而指出的差别之外,泛型类声明的规则也适用于泛型结构声明。

20.3泛型接口声明

接口也可以定义可选的类型参数

interface-declaration:(接口声明:)
attribute opt interface-modifiers opt interface indentifier type-parameter-list opt 
interface-base opt type-parameter-constraints-clause opt interface-body;
(特性可选 接口修饰符可选 interface 标识符 类型参数列表可选 基接口可选 类型参数约束语句可选 接口体;可选)
使用类型参数声明的接口是一个泛型接口声明。除了所指出的那些,泛型接口声明遵循和常规结构声明相同的规则。
ログイン後にコピー

在接口声明中的每个类型参数在接口的声明空间定义了一个名字。在一个接口上的类型参数的作用域包括基接口、类型约束语句和接口体。在其作用域之内,一个类型参数可以被用作一个类型。应用到接口上的类型参数和应用到类(§20.1.1)上的类型参数具有相同的限制。

在泛型接口中的方法与泛型类(§20.1.8)中的方法遵循相同的重载规则。

20.3.1实现接口的唯一性

由泛型类型声明实现的接口必须为所有可能的构造类型保留唯一性。没有这条规则,将不可能为特定的构造类型确定调用正确的方法。例如,假定一个泛型类声明允许如下写法。

interface I<T>
{
void F();
}
class X<U, V>:I<U>,I<V> //错误,I<U>和I<V>冲突
{
void I<U>.F(){…}
void I<V>.F(){…}
}
ログイン後にコピー

如果允许这么写,那么下面的情形将无法确定执行那段代码。

I<int> x = new X<int ,int>();
x.F();
ログイン後にコピー

为了确定一个泛型类型声明的接口列表是有效的,可以按下面的步骤进行。

让L成为在泛型类、结构或接口声明 C中指定的接口的列表。

将任何已经在L中的接口的基接口添加到L

从L中删除任何重复的接口

在类型实参被替换到L后,如果任何从C创建的可能构造类型,导致在L中的两个接口是同一的,那么C的声明是无效的。当确定所有可能的构造类型时,约束声明不予考虑。


在类声明X之上,接口列表L由I和I组成。该声明是无效的,因为任何使用相同类型U和V的构造类型,将导致这两个接口是同一的。

20.3.2显式接口成员实现

使用构造接口类型的显式接口成员实现本质上与简单接口类型方式上是相同的。和以往一样,显式接口成员实现必须由一个指明哪个接口被实现的接口类型而限定。该类型可能是一个简单接口或构造接口,如下例子所示。

interface IList<T>
{
T[] GetElement();
}
interface IDictionary<K,V>
{
V this[K key];
Void Add(K key , V value);
}
class List<T>:IList<T>,IDictionary<int , T>
{
T[] IList<T>.GetElement(){…}
T IDictionary<int , T>.this[int index]{…}
void IDictionary<int , T>.Add(int index , T value){…}
}
ログイン後にコピー

[1] 也就是在类型参数被替换成类型实参时,有可能替换后的实参导致出现两个成员使用相同的名字和签名。


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

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C言語で特殊文字を処理する方法 C言語で特殊文字を処理する方法 Apr 03, 2025 pm 03:18 PM

C言語では、以下などのエスケープシーケンスを通じて特殊文字が処理されます。\ nはラインブレークを表します。 \ tはタブ文字を意味します。 ESACEシーケンスまたは文字定数を使用して、Char C = '\ n'などの特殊文字を表します。バックスラッシュは2回逃げる必要があることに注意してください。さまざまなプラットフォームとコンパイラが異なるエスケープシーケンスを持っている場合があります。ドキュメントを参照してください。

C文字列におけるcharの役割は何ですか C文字列におけるcharの役割は何ですか Apr 03, 2025 pm 03:15 PM

Cでは、文字列でCharタイプが使用されます。1。単一の文字を保存します。 2。配列を使用して文字列を表し、ヌルターミネーターで終了します。 3。文字列操作関数を介して動作します。 4.キーボードから文字列を読み取りまたは出力します。

C言語でさまざまなシンボルを使用する方法 C言語でさまざまなシンボルを使用する方法 Apr 03, 2025 pm 04:48 PM

c言語のシンボルの使用方法は、算術、割り当て、条件、ロジック、ビット演算子などをカバーします。算術演算子は基本的な数学的操作に使用されます。割り当てと追加、下位、乗算、除算の割り当てには、条件操作に使用されます。ポインター、ファイル終了マーカー、および非数値値。

C言語のcharとwchar_tの違い C言語のcharとwchar_tの違い 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の文字範囲が大きく、特別な機能が算術演算に使用されます。

マルチスレッドと非同期C#の違い マルチスレッドと非同期C#の違い Apr 03, 2025 pm 02:57 PM

マルチスレッドと非同期の違いは、マルチスレッドが複数のスレッドを同時に実行し、現在のスレッドをブロックせずに非同期に操作を実行することです。マルチスレッドは計算集約型タスクに使用されますが、非同期はユーザーインタラクションに使用されます。マルチスレッドの利点は、コンピューティングのパフォーマンスを改善することですが、非同期の利点はUIスレッドをブロックしないことです。マルチスレッドまたは非同期を選択することは、タスクの性質に依存します。計算集約型タスクマルチスレッド、外部リソースと相互作用し、UIの応答性を非同期に使用する必要があるタスクを使用します。

C言語でCharを変換する方法 C言語でCharを変換する方法 Apr 03, 2025 pm 03:21 PM

C言語では、charタイプの変換は、キャスト:キャスト文字を使用することにより、別のタイプに直接変換できます。自動タイプ変換:あるタイプのデータが別のタイプの値に対応できる場合、コンパイラは自動的に変換します。

C言語合計の機能は何ですか? C言語合計の機能は何ですか? Apr 03, 2025 pm 02:21 PM

C言語に組み込みの合計機能はないため、自分で書く必要があります。合計は、配列を通過して要素を蓄積することで達成できます。ループバージョン:合計は、ループとアレイの長さを使用して計算されます。ポインターバージョン:ポインターを使用してアレイ要素を指し示し、効率的な合計が自己概要ポインターを通じて達成されます。アレイバージョンを動的に割り当てます:[アレイ]を動的に割り当ててメモリを自分で管理し、メモリの漏れを防ぐために割り当てられたメモリが解放されます。

C言語でchar配列の使用方法 C言語でchar配列の使用方法 Apr 03, 2025 pm 03:24 PM

Char Arrayは文字シーケンスをC言語で保存し、char array_name [size]として宣言されます。アクセス要素はサブスクリプト演算子に渡され、要素は文字列のエンドポイントを表すnullターミネーター「\ 0」で終了します。 C言語は、strlen()、strcpy()、strcat()、strcmp()など、さまざまな文字列操作関数を提供します。

See all articles