C# 2.0 사양(제네릭 5)
일반 4개에 연결
20.6.5 구문 모호성
§20.9.3 및 §20.9.4에서는 simple-name 및 member-access(member-access)가 쉽습니다. 표현에 문법적 모호성을 야기합니다. 예를 들어
F(G<A,B>(7));
{ } ] > : ; , . ?
">"는 유형 인수 목록으로 구문 분석됩니다. 그렇지 않으면 ">"가 연산자로 구문 분석됩니다.
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语句中相同的类型不能被指定多次。
在很多情况下,约束可以包含任何关联类型的类型参数或者方法声明作为构造类型的一部分,并且可以包括被声明的类型,但约束不能是一个单一的类型参数。
被指定作为类型参数约束的任何类或者接口类型,作为泛型类型或者被声明的方法,必须至少是可访问的(§10.5.4)。
如果一个类型参数的where 语句包括new()形式的构造函数约束,则使用new 运算符创建该类型(§20.8.2)的实例是可能的。用于带有一个构造函数约束的类型参数的任何类型实参必须有一个无参的构造函数(详细情形参看§20.7)。
下面是可能约束的例子
interface IPrintable { void Print(); } 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:
- 同一转换(§6.1.1)
- 隐式引用转换(§6.1.4)
- 装箱转换(§6.1.5)
- 从类型参数A到C(§20.7.4)的隐式转换。
如果约束是new(),类型实参A不能是abstract并,且必须有一个公有的无参的构造函数。如果如下之一是真实的这将可以得到满足。
- A是一个值类型(如§4.1.2中所描述的,所有值类型都有一个公有默认构造函数)。
- A是一个非abstract类,并且 A包含一个无参公有构造函数。
- A是一个非abstract类,并且有一个默认构造函数(§10.10.4)。
如果一个或多个类型参数的约束通过给定的类型实参不能满足,将会出现编译时错误。
因为类型参数不被继承,同样约束也决不被继承。在下面的例子中,D 必须在其类型参数T上指定约束,以满足由基类B
List<T>实现了IEnumerable接口。 class B<T> where T: IEnumerable{…} class D<T>:B<T> where T:IEnumerable{…} class E<T>:B<List<T>>{…}
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>(); } }
程序的输出如下
1 2 3
尽管推荐不要让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到I的任何基接口类型的转换。在运行时,如果T是一个值类型,这将作为一个装箱转换而进行。否则,它将作为一个隐式地引用转换而进行。
如果类型参数T指定类型C作为约束,将存在下面的附加转换:
从T到C的隐式引用转换,从T到任何C从中派生的类,以及从T到任何从其实现的接口。
从C到T的显式引用转换,从C从中派生的类[3]到T,以及C实现的任何接口到T
如果存在从C 到A的隐式用户定义转换,从T到 A的隐式用户定义转换。
如果存在从A 到C的显式用户定义转换,从A到 T的显式用户定义转换。
从null类型到T的隐式引用转换
一个带有元素类型的数组类型T具有object和System.Array之间的相互转换(§6.1.4,§6.2.3)。如果T有作为约束而指定的类类型,将有如下附加规则
从带有元素类型T的数组类型AT到带有元素类型U的数组类型AU的隐式引用转换,并且如果下列二者成立的话,将存在从AU到AT显式引用转换:
- AT和AU 有相同数量的维数。
- U是这些之一:C,C从中派生的类型,C所实现的接口,作为在T上的约束而指定的接口I,或I的基接口。
先前的规则不允许从非约束类型参数到非接口类型的直接隐式转换,这可能有点奇怪。其原因是为了防止混淆,并且使得这种转换的语义更明确。例如,考虑下面的声明。
class X<T> { public static long F(T t){ return (long)t; // ok,允许转换 }
}
如果t到int的直接显式转换是允许的,你可能很容易以为X
class X<T> { public static long F(T t) { return (long)(object)t; //ok;允许转换 } }
[1] 这种情况下“>”被解释为大于运算符。
[2] 在程序中重写ToString时,一般不推荐添加这种类似的计算逻辑,因为它的这种结果变化不易控制,增加了调试程序的复杂性。
[3] C的基类或其基类的基类等。
以上就是C# 2.0 Specification(泛型五)的内容,更多相关内容请关注PHP中文网(www.php.cn)!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











C 언어에서 특수 문자는 다음과 같은 탈출 시퀀스를 통해 처리됩니다. \ n 라인 브레이크를 나타냅니다. \ t는 탭 문자를 의미합니다. char c = '\ n'과 같은 특수 문자를 나타 내기 위해 탈출 시퀀스 또는 문자 상수를 사용하십시오. 백 슬래시는 두 번 탈출해야합니다. 다른 플랫폼과 컴파일러마다 다른 탈출 시퀀스가있을 수 있습니다. 문서를 참조하십시오.

C에서 숯 유형은 문자열에 사용됩니다. 1. 단일 문자를 저장하십시오. 2. 배열을 사용하여 문자열을 나타내고 널 터미네이터로 끝납니다. 3. 문자열 작동 함수를 통해 작동합니다. 4. 키보드에서 문자열을 읽거나 출력하십시오.

C 언어에서 char와 wchar_t의 주요 차이점은 문자 인코딩입니다. char ascii를 사용하거나 ascii를 확장하고, wchar_t는 유니 코드를 사용합니다. Char는 1-2 바이트를 차지하고 WCHAR_T는 2-4 바이트를 차지합니다. Char는 영어 텍스트에 적합하며 WCHAR_T는 다국어 텍스트에 적합합니다. Char_t는 널리 지원되며, 컴파일러 및 운영 체제가 유니 코드를 지원하는지 여부에 따라 다릅니다. Char는 문자 범위가 제한되며 WCHAR_T는 더 큰 문자 범위를 가지며 특수 함수는 산술 작업에 사용됩니다.

C 언어 커버 산술, 할당, 조건, 논리, 비트 연산자 등의 기호의 사용 방법은 기본 수학 연산에 사용되며, 할당 연산자는 할당 및 추가, 곱하기, 분할 할당에 사용되며, 곱하기 및 분할 할당에 사용되며, 조건에 따라 조건 운영자가 사용되며, 비트 오퍼레이터에 사용되며, 스페셜 오퍼레이터는 비트 수준의 운영에 사용됩니다. 포인터, 파일 종료 마커 및 비수통 값.

멀티 스레딩과 비동기식의 차이점은 멀티 스레딩이 동시에 여러 스레드를 실행하는 반면, 현재 스레드를 차단하지 않고 비동기식으로 작업을 수행한다는 것입니다. 멀티 스레딩은 컴퓨팅 집약적 인 작업에 사용되며 비동기식은 사용자 상호 작용에 사용됩니다. 멀티 스레딩의 장점은 컴퓨팅 성능을 향상시키는 것이지만 비동기의 장점은 UI 스레드를 차단하지 않는 것입니다. 멀티 스레딩 또는 비동기식을 선택하는 것은 작업의 특성에 따라 다릅니다. 계산 집약적 작업은 멀티 스레딩을 사용하고 외부 리소스와 상호 작용하고 UI 응답 성을 비동기식으로 유지 해야하는 작업을 사용합니다.

C 언어에서 숯 유형 변환은 다른 유형으로 직접 변환 할 수 있습니다. 캐스팅 : 캐스팅 캐릭터 사용. 자동 유형 변환 : 한 유형의 데이터가 다른 유형의 값을 수용 할 수 있으면 컴파일러가 자동으로 변환됩니다.

C 언어에는 내장 합계 기능이 없으므로 직접 작성해야합니다. 합계는 배열 및 축적 요소를 가로 질러 달성 할 수 있습니다. 루프 버전 : 루프 및 배열 길이를 사용하여 계산됩니다. 포인터 버전 : 포인터를 사용하여 배열 요소를 가리키며 효율적인 합계는 자체 증가 포인터를 통해 달성됩니다. 동적으로 배열 버전을 할당 : 배열을 동적으로 할당하고 메모리를 직접 관리하여 메모리 누출을 방지하기 위해 할당 된 메모리가 해제되도록합니다.

char 어레이는 문자 시퀀스를 C 언어로 저장하고 char array_name [size]로 선언됩니다. 액세스 요소는 첨자 연산자를 통해 전달되며 요소는 문자열의 끝점을 나타내는 널 터미네이터 '\ 0'으로 끝납니다. C 언어는 strlen (), strcpy (), strcat () 및 strcmp ()와 같은 다양한 문자열 조작 함수를 제공합니다.
