值类型,良构类型,异常处理
值类型
结构
- 值类型:直接包含值,变量引用的位置就是值在内存中实际的存储位置。
- 引用类型:它的值是对一个的对象实例的应用。
- 声明结构:struct { }
- 包括属性、字段、方法、构造器。但是不可以包含用户定义的默认构造器
装箱
- 步骤:
- 首先在堆内存上分配好空间。(用于存放值类型的数据以及少许额外开销)
- 接着发送一次内存复制动作。(当前存储位置的值类型数据复制到堆上分配好的位置)
- 最后转化结果是对堆上的新存储位置的引用。
相反的过程就是拆箱
枚举
- 枚举的关键特征是在编译时声明了一组可以通过名称来引用的常量值。用其来代替布尔值能改善可读性。
- 一个默认为0,后面的根据前一个的值来确定自己的值。
良构类型
重写object的成员
重写object的成员
-
重写ToString():默认情况下对象上调用其会返回类的完全限定名称。
- 改写为返回有用的值
-
重写GetHashCode()
无论如何重写Equals()就要重写GetHashCode()
hash code的作用就是生产与对象值对应的数字,从而高效地平衡散列表有一下几大原则
- 必须:相等的对象必须有相等的散列码
- 必须:在特定对象的生存期内,GetHashCode必须总是成功返回一个值,即使对象的数据发送了变化。
- 必须:GetHashCode不应引发任何异常,其总是成功返回一个值
-
重写Equals():
对象同一性和相同的对象值:两个同一的引用显然是相等的,然而,两个应用不相等的对象也可能是相等的对象。对象标识不同,不一定标识数据不同。
步骤:
- 检查是否为null
- 如果是引用类型就检查引用是否相等
- 检查数据类型是否相同
- 可能要检查散列码是否相等
- 如果基类重写了Equals()就检查base.Equals()。
- 比较每一个标识字段,判断是否相等。
- 重写GetHashCode()
- 重写==和!=操作符。
操作符重载
- 实现显式和隐式转换操作符并不是对转型操作符(())进行重载。但是效果一样。
- 实现转换操作符时,为了保证其封装线,要么返回值,要么参数必须是封闭类型。C#不允许在被转化类型的作用域以外指定转换。
定义命名空间
- 为了防止名称冲突和在引用第三方的程序集时可能造成的大量的冲突。要为类文件进行一定程度的命名
- 要为命名空间名称附加公司名前缀。
- 要为命名空间名称中的二级名称使用稳定的,不随版本升级而变化的产品名称。
- 不要定义没有明确放到一个命名空间中的类型。
- 考虑创建于命名空间层次结构相匹配的文件夹结构
异常处理
异常处理语句
1、throw语句:throw语句用于主动引发一个异常,使用throw语句可以在特定的情形下,自行抛出异常。
throw ExObject; //ExObject是所要抛出的异常对象,该对象是派生自System.Exception类的类对象。
2、try…catch语句
try块里的代码是要来检测的代码,有可能引发一个异常。如果真的引发了异常,那么某个catch块就要尝试处理这个异常,可以同时存在多个catch块,用来捕获不同的错误信息。而finally块的作用是提供一个最终位置,在其中放入无论是否发生异常都要执行的代码。finally块最适合用来执行资源清理。无论try块中的代码是否引发一个异常,finally块都会执行。
try
{
//被监控的代码
}
catch(异常类名 异常变量)
{
//异常处理
}
3、try…catch…finally
try
{
//被监控的代码
}
catch(异常类名 异常变量)
{
//异常处理
}
finally
{
//无论监控代码是否异常都要执行的代码
}
一个当除以零时抛出异常的实例
using System;
namespace ErrorHandlingApplication
{
class DivNumbers
{
int result;
DivNumbers()
{
result = 0;
}
public void division(int num1, int num2)
{
try
{
result = num1 / num2;
}
catch (DivideByZeroException e)
{
Console.WriteLine("Exception caught: {0}", e);
}
finally
{
Console.WriteLine("Result: {0}", result);
}
}
static void Main(string[] args)
{
DivNumbers d = new DivNumbers();
d.division(25, 0);
Console.ReadKey();
}
}
}
代码运行的效果
- 另外也可以自己定义自己的异常用户自定义的异常类是派生自ApplicationException类。下面的实例演示了这点:
using System;
namespace UserDefinedException
{
class TestTemperature
{
static void Main(string[] args)
{
Temperature temp = new Temperature();
try
{
temp.showTemp();
}
catch(TempIsZeroException e)
{
Console.WriteLine("TempIsZeroException: {0}", e.Message);
}
Console.ReadKey();
}
}
}
public class TempIsZeroException: ApplicationException
{
public TempIsZeroException(string message): base(message)
{
}
}
public class Temperature
{
int temperature = 0;
public void showTemp()
{
if(temperature == 0)
{
throw (new TempIsZeroException("Zero Temperature found"));
}
else
{
Console.WriteLine("Temperature: {0}", temperature);
}
}
}
效果:
Exception类和派生类
- System.Exception类是所有异常类的基类,所以可以直接用catch(Exception ex)来捕获异常信息,但为了捕获有关异常的具体信息,我们用更具体的派生类型来处理错误。
- C#中如果同时存在多个catch块,为了捕获具体的异常信息,必须从“最具体”到“最不具体”排列。也就是说,用于捕获Exception的catch语句不能出现在捕获FormatException的catch语句之前,因为FormatException比Exception更为具体。
- 一些常见的预定义的异常类
Exception | 应用程序执行期间发生错误 | |
---|---|---|
SystemException | 系统异常 | 所有Exception的基类 |
ArgumentException | 当方法提供的任意一个参数无效时,引发此异常 | |
ArithmeticException | 算数导致的错误 | |
DataException | 在使用ADO.NET组件时生成错误 | System.Data命名空间提供 |
FormatException | 参数的格式不符合被调用方法的参数规范 | |
IOException | IO错误 | System.IO命名空间提供 |
IndexOutOfRangeException | 数组越界 | |
ArgumentNullException | 空引用传递给无效参数的方法时引发 | 对应JAVA的空指针 |
DivideByZeroException | 0为除数 | |
OverflowException | 运算结果过大,无法以目标格式保存 | 比如把一个30位数字赋给int |
ApplicationException | 应用程序执行过程中检测到由应用程序定义的异常 | |
TargetException | 试图调用无效目标时 | System.Reflection命名空间提供 |