The C# Programming Language(Third Edition) Part III
结构
结构类型的变量直接包含了结构的数据,而类类型的变量包含的是指向数据的引用,而这个数据就是对象。
类和结构的区别:
结构式值类型。所有的结构类型都隐式地继承自System.ValueType类。对一个结构类型变量的赋值会创建一个被赋值的复制。结构的默认值是将所有的值类型字段设为默认值,并将所有的引用类型字段设为null。装箱和拆箱操作可用来在结构类型和object类型之间转换。对于结构来说this的含义是不同的。结构的实例字段声明不可以包含变量初始化语句。结构不可以声明无参的实例构造函数。结构不可以声明析构函数。
一个结构所依赖的完整结构集合石直接依赖关系的传递闭包。
在类的实例构造函数或实例函数成员里,this被归类为一个值。虽然this可以在被调用的函数成员里用来指向实例,但是类的函数成员里是不可以对this进行赋值的。在结构的实例构造函数里,this对应的是一个结构类型的out参数。在结构的实例函数成员里,this对应的是一个结构类型的ref参数。this都被归类为一个变量,所以通过对this的赋值或是将它作为ref或out参数传递,是完全可以修改整个结构的。
结构是不允许实例字段声明包含变量初始化语句的。
struct Point
{
public int x = 1; // Error, initializer not permitted
public int y = 1; // Error, initializer not permitted
}
“可以绕过这一限制,
struct Point
{
bool initialized;
int x;
public int X{
get{
if(!initialized) { x = 1, initialized = true; }
return x;
}
}
}
和类不同,结构不允许声明无参的实例构造函数。每个结构都隐式地含有一个无参的实例构造函数,它总是会将所有的值类型字段设为默认值,而降所有引用类型字段设为inull。直到结构里所有的字段都被明确赋值之前,任何实例成员函数都不能被调用。
数组
数组时一个包含了很多变量的数据结构,所有的变量都是通过计算下标来访问的。包含在数组里的变量都具有相同的类型,叫做数组的元素类型。
数组的秩决定了每个数组元素小标的个数。数组的秩也叫做数组的维度。秩为一得数组就是一维数组。秩大于一得数组就是多维数组。
秩说明符是从左至右读取的。int[][,,][,]类型是一个一维数组,其类型时一个三维数组,三维数组的类型是二维数组,最后二维数组的类型才是int。
System.Array类型时所有数组类型的抽象基础类型。任何数组类型到System.Array都存在隐式引用转换。其本身不是一个数组类型,而是一个类类型。
一维数组T[]实现了IList<T>及其基础接口。所以从T[]到IList<T>及其基础接口存在一个隐式转换。
数组协变
对于任意两个引用类型A和B,如果从A到B存在隐式引用转换或显示转换,那么从数组类型A[R]到数组类型B[R]也存在相同的引用转换。这个关系就称为数组协变。
数组协变的意思是,一个数组类型为A[R]的值实际上可能是一个指向数组类型B[R]的实例的引用。
int i = 3;
int[] y = new int[i] {0,1,2}; //error,i not a constant
接口
接口定义的是一份合约。实现了某个接口的类或者结构必须遵守这份合约。
接口不管是直接还是间接地继承自己,都是一个编译期错误。
所有的接口都隐式含有公共访问权限。
接口方法声明不可以指定方法主体,所以声明必须以分号结尾。
接口成员访问:
interface IList
{
int Count { get; set; }
}
interface ICounter
{
void Count(int i);
}
interface IListCounter: IList, ICounter {}
class C
{
void Test(IListCounter x) {
x.Count(1); // Error
x.Count = 1; // Error
((IList)x).Count = 1; // Ok, invokes IList.Count.set
((ICounter)x).Count(1); // Ok, invokes ICounter.Count
}
}
有些情况下,接口成员的名字可能不适合实现类,这时接口成员就可以通过显示接口成员实现来实现。
接口映射:
一个类或结构必须为在它基础列表里列出的所有成员都提供一份实现。在一个实现类或结构里查找接口成员实现的过程就称为接口映射。
如果没有显示重新实现一个接口,继承类是不可能改变从基础类里继承而来的接口映射的。
但是,当接口方法被映射到一个类里的虚拟方法上时,继承类就有可能覆写虚拟方法,从而改变接口的实现。
继承了接口的实现的类可以通过将它包含在基类列表里来重新实现这个接口。
继承接口映射对在重新实现的接口上建立的接口映射是完全没有影响的。
继承的公共成员声明和继承的显示接口成员声明都会参与重新实现的接口的接口映射过程。
当类实现了一个接口时,它同时会隐式实现那个接口的所有基础接口。重新实现的接口也一样会显示地重新实现这个接口的所有基础接口。
枚举
枚举是一种特殊的值类型,它可以声明一组已命名常量。
在微软实现的C#里,枚举成员不可以取名为value_,因为这个名字被枚举的内部表示保留了。
枚举成员的关联值可以是隐式赋值的,也可以是显示赋值的。如果枚举成员的声明里含有常量表达式初始化语句,那么常量表达式的值就会和枚举成员的值相关联。
System.Enum类型时所有枚举类型的抽象基类,任何枚举类型都可以访问到System.Enum里继承而来的成员。
委托
委托实例封装了一个调用列表,这是一个包含了一或多个方法的列表,每一个都指向一个可调用的实体。
异常
C#里,所有的异常都必须是一个继承自System.Exception的类类型实例。
在C#里,可以用finally块来编写一些不管正常执行还是抛出异常的时候都会执行的终止代码。
在C#里,系统级别的异常都和应用程序级别上的错误条件一样,拥有定义完整的异常类。
try语句负责处理异常。当异常发生时,系统会根据异常的运行时类型,搜索最近的可以处理它的catch子句。
特性
C#允许程序员为在程序里声明的实体指定说明信息。C#允许程序员发明新的说明信息,称为特性。然后程序员就可以把这些特性附加到各种不同的程序实体上,并在运行时环境里获取这些特性信息。
一个类无论是否直接继承自抽象类System.Attribute,它都是一个特性类。特性类声明定义了一个新类型的特性,这个特性可以被放置在其他声明之上。
特性实例是一个在运行时表示特性的实例。特性是由特性类、位置实参和已命名实参定义的。特性实例就是特性类用位置和命名实参初始化出来的实例。
AttribteUsage用来描述特性类可以使用的方法。
ConditionalAttribute用来定义条件方法。
ObsoleteAttribute用来将一个成员标记为过时的。
.Net运行时提供了大量的特性来让C#程序员可以喝COM和Win32 DLL组件互操作。
不安全代码
在和底层操作系统交互的时候,在访问一个内存映射设备的时候,或则实现一个对实时性要求很高的算法时,如果不访问指针几乎就不肯定或不现实的。基于这个原因,C#提供了编写不安全代码的能力。
C#里的不完全特性只能通过不安全上下文来访问。不安全上下文引入的方法是在声明类型或成员的时候包含一个unsafe修饰符。
在不安全上下文里,类型可以是指针类型、值类型或者引用类型。
指针类型例子:
Example |
Description |
byte* |
Pointer to byte |
char* |
Pointer to char |
int** |
Pointer to pointer to int |
int*[] |
Single-dimensional array of pointers to int |
void* |
Pointer to unknown type |
int * pi, pj; //not as int *pi,*pj;
类型T*的指针值代表的是类型T变量的地址。指针间接寻址操作符*可以用来访问这个变量。
去地址操作符和fixed语句将变量分为两个类别:固定变量和可移动变量。&操作符可以不受限制地获取固定变量的地址。
一元操作符*的作用是指针间接寻址,它可以用来获得指针所指向的变量。
指针成员访问由一个基础表达式,加上一个"->"标记和一个标识符组成。
sizeof操作符返回一个给定类型的变量所占用的字节数。
Expression |
Result |
sizeof(sbyte) |
1 |
sizeof(byte) |
1 |
sizeof(short) |
2 |
sizeof(ushort) |
2 |
sizeof(int) |
4 |
sizeof(uint) |
4 |
sizeof(long) |
8 |
sizeof(ulong) |
8 |
sizeof(char) |
2 |
sizeof(float) |
4 |
sizeof(double) |
8 |
sizeof(bool) |
1 |
在不安全上下文里,嵌套语句产生式允许使用一个额外的构造(fixed语句)来“固定住”一个可移动的变量。
定长缓冲区是一个表示拥有给定类型的一个固定长度的缓冲区变量存储成员。
在不安全上下文里,局部变量声明可以包含一个栈分配初始化语句,它会在调用栈上分配内存。
stackalloc T[E]形式的栈分配初始化语句要求T是非托管类型,并且E是int类型的表达式。
作 者:doku
出 处:http://www.cnblogs.com/kulong995/
关于作者:喜欢编程,喜欢美食,专注于.NET项目开发。
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是作者坚持原创和持续写作的最大动力!