1. 装箱和拆箱
由于在C#中所有类型(包括值类型)都继承于System.Object类。另一方面Object类本身又是引用类型。所以值类型的数据可以隐式转换为终极基类的Object类型,同时数据由值类型变成了引用类型,即所谓的装箱。同理由于值类型也实现一些接口,当值类型数据隐式转换成相应的接口类型时,也属于装箱。而反向操作,将装箱的值类型数据显式转换回来就是拆箱。显然要有装箱操作,才有拆箱操作。如果尝试对null引用拆箱或拆箱时数据类型不对的话,会报错。
int i = 123;
object o = i;//装箱
int j = (int)o;//拆箱
float k = (float)o;//也是拆箱
相对于简单的赋值而言,装箱和拆箱过程需要进行大量的计算,应尽量避免。
2. 集合(动态数组)
由于数组要在一开始指定数组的大小,不能添加多于数组大小的数据。于是C#提供了可以的动态改变存取数组大小的集合类。常用的有:arrayList,List,HashTable,Dictionary<Tkey,TValue>,Stack和Queue。
1. ArrayList & List
ArrayList和List的用法基本一致,差别仅是ArrayList里存的是object类数据,所以实际可以存任何数据(存值类型数据时自动装箱),List是泛型类,需要在声明时指定存储的类型。由于ArrayList可能有装箱和拆箱的操作,相对耗性能,而且有可能拆箱是转换失败而报异常(不是类型安全)。官方推荐尽量不使用ArrayList,即使要存不同类型的对象,也推荐使用List。
-
常用属性:
Count :ArrayList中包含元素的个数。
Capacity :ArrayList现阶段可容元素的大小。
Capacity一开始默认为4。在元素个数满的时候自动翻倍(x2)。
也可以通过构造函数指定一开始的大小。同样在元素个数满的时候也会自动翻倍。也可以直接修改Capacity,但不能小于Count。不然会报错。
列表名[index]:可以通过索引提取和修改数据。 -
常用方法:
Add(data):添加数据到末尾
Insert(Index,data):添加数据到索引Index。
Clear():清空列表(不会缩小容器大小)
Remove(data):清除第一个匹配的数据
RemoveAt(index):删除索引位置的数据
Reverse():倒序元素
IndexOf(data):返回第一个匹配的元素的索引。
2. Hashtable/Dictonary<Tkey,TValue>
和ArrayList和List差不多,Dictonary是Hashtable的泛型版本。同样不推荐使用Hashtable。以Hashtable为例,每个元素存在一个键/值对。作为键的对象必须是唯一不可变的,且不能为null。值可以重复。
-
常用属性:
Count:获取键值对个数。
集合名[index]:可以通过索引提取和修改数据。 -
常用方法:
Add(键,值):添加带有指定键和值的元素,键已存在时会报错
Clear():清空所有元素。
Remove(键):移除相应的键值对元素。
ContainsKey(键):确认是否包含该键。
ContainsValue(值):确认是否包含该值。
3. Stack和Queue
Stack和Queue都有非泛型和泛型版本(Stack和Queue),同样推荐使用泛型版本。
栈Stack是先进后出的数据结构,就像一堆堆叠的椅子。每次只能取出最后叠上的椅子。想要取出最早放入的椅子,只能把它之后放入的椅子都取出来。汉诺塔就是三个栈结构。
常用方法:
Push(data):往栈顶放数据。
Pop():返回并删除顶部数据。
Peek():返回但不删除顶部数据。
队列Queue是先进先出的数据结构,就如排队,先获取的是先放入的元素。
常用方法:
Enqueue(data):往队尾加数据。
Dequeue():返回并删除队首的数据。
Peek():返回但不删除队首的数据。
3. 异常处理
程序错误类型:
1) 语法错误:在编译期间就报错。
2) 异常:在程序运行时出错非正常中止。
3) 逻辑错误:程序正常运行,但结果和预期不一样。
C#中也将各种异常封装为类,其基类为Exception类。在程序遇到问题时,就抛出(throw)相应的异常实例。之后程序自动中止。
C#提供了try-catch-finally语法来捕获异常,进行相应处理,同时让程序不中止。
try
{
//可能异常的代码
}
catch(Exception e)//捕获异常
{
//捕获到异常时运行的代码
}
finally//可选
{
//无论是否捕获到异常都会运行的代码
}
在catch()括号中可以写具体的异常类,写Exception即捕获所有异常。后面的e变量为捕获到异常的对象。可以通过变量输出异常的信息和位置。
catch(Exception e)
{
Debug.Log(e.Message);//输出异常的信息
Debug.Log(e.StackTrace);//输出异常的位置
}
不想操作异常变量时可以直接写异常类型
catch(Exception)
{
…}
如果可能发生多种异常,想对不同异常进行不同处理,可以写复数个catch的语句。
catch(Exception1)
{
…}
catch(Exception2)
{
…}
catch(Exception)
{
…}
由于catch语句是从上往下顺序比对执行。比对到一个就执行该catch语句,而不会再执行其他语句。所以Exception父类的语句要放最后(如果有的话)。
注意:一旦try里的语句发生异常后,异常语句之后的语句就都不会执行了。
我们也能自己定义异常,只要继承Exception基类就好了。
class EqualZeroException: Exception//等于零异常
{
public EqualZeroException()
{
base.Message