深层次详解Exception
在Ilustrustor C# 2008中这样描述Exception:The BCL defines a number of exception classes,each representing a specific type.When one occurs,the CLR
所有的异常类都继承自System.Exception类,当异常产生时,CLR将创建该异常类的实例对象,将从最底层依次寻找合适的异常类型,同时若存在catch语句时将会选择最合适的语句进行处理。
catch语句包括三种形式:
/*第一种:通用型*/ catch { //Statements } /*第二种:特殊类型*/ catch(ExceptionType) { //Statements } /*第三种:带对象的特殊类型*/ catch(ExceptionType InstanceID) { //Statements } |
若使用第三种类型,可以得到异常实例 InstanceID的相关属性,如Message、Source等。
多catch的处理原则
一个异常发生时,会跳转到与异常异常最匹配的catch块执行,继承链决定了匹配度。
/// <summary> /// 选择处理异常类 /// </summary> public static void HandleExceptionMethod() { try { throw new ApplicationException("ApplicationException Occur!"); } catch (NullReferenceException) { //Handle1 Console.WriteLine("Handle1"); } catch (ArgumentException) { //Handle2 Console.WriteLine("Handle2"); } catch (ApplicationException) { //Handle3 Console.WriteLine("Handle3"); } catch (SystemException) { //Handle4 Console.WriteLine("Handle4"); } catch (Exception) { //Handle5 Console.WriteLine("Handle5"); } Console.ReadKey(); } |
运行结果

在上例中共有5个catch块,注意catch块必须按照从最具体到最常规的顺序排列,否则可能造成编译错误。例若将Handle5的向上移到Handle4前面,则发生编译错误

关于throw
在上例中我们实际上已经使用了throw来手工抛出异常,在C#中发生异常时隐含CLR会throw(引发)异常。这句话里我们注意到都是使用throw。因此在实际应用中throw常用于重新引发已捕获的异常及显式引发异常,比如上例中就是显式引发异常。
/// <summary> /// throw /// </summary> public class ThrowDemo { public static void ThrowDemoMethod() { FileStream fs = null; try { fs = new FileStream("c:\\data.txt", FileMode.Open);//该文件并不存在 StreamReader sr = new StreamReader(fs, System.Text.Encoding.Default, false); string line; //A value is read from the file and output to the console. line = sr.ReadLine(); Console.WriteLine(line); } catch (FileNotFoundException) { throw new FileNotFoundException("data.txt is not existed"); } finally { fs.Close(); } } } |
编译器在编译时,将会产生一个FileNotFoundException,进入catch处理,catch语句中重新触发了此异常,并提示“data.txt is not existed"。执行结果如图

注意异常中的提示文字并不相同:第二次是系统异常的提示文字。
另附 Exception层次图(原图见http://book.javanb.com/From-Java-To-Csharp-A-Developers-Guide/0321136225_ch13lev1sec2.html)

使用new重写基类成员
继承类不能删除基类中的成员,但可以使用new关键字来隐藏基类的该成员;new关键字可以显式隐藏从基类继承的成员;若不使用new关键字直接声明同名成员则会出现警告,例:
class BaseClass { protected int age = 23; } class InheritClass : BaseClass { protected int age = 22; } |

使用new的同时也可以修改基类成员的访问修改类型,完整示例如下:
.png)
使用new实质是对基类成员的重写,对IL底层机制来说,new修饰符不会影响代码的编译,它唯一的作用只是移除编译器的警告。
new与override区别
两者都可以实现对基类的重写,从形式上讲区别是:
1、override需要基类中声明为virtual类型;
2、override重写方法不可更改方法访问类型;
另外值得引起注意的是new重写方法是在脱离基类在继承而中产生新的方法,基类方法与继承方法之间无任何联系,即继承类从基类中复制了基类方法的副本,两个方法之间不存在引用关系;override实质是对基类方法的重写。
class VirtualClass { public string name = "Wang Hongjian"; public virtual void DisplayName() { Console.WriteLine("My name is {0}", name); } } /*new重写*/ class NewVirtualClass : VirtualClass { new public void DisplayName() { Console.WriteLine("New Method:My name is {0}", base.name); } } /*override重写*/ class InheritVirtualClass : VirtualClass { public override void DisplayName() { Console.WriteLine("Override Method:My name is {0}", base.name); } } class Program { static void Main(string[] args) { /*new重写*/ BaseClass baseClass = new BaseClass(); baseClass.DisplayMyAge(); InheritClass _class = new InheritClass(); _class.DisplayMyAge(); Console.WriteLine(); /*new重写与virtual重写*/ VirtualClass virtualClass=new VirtualClass(); virtualClass.DisplayName(); VirtualClass newVirtualClass = new NewVirtualClass(); virtualClass.DisplayName();//注意此处结果 InheritVirtualClass _virtualClass = new InheritVirtualClass(); _virtualClass.DisplayName(); } } |
运行结果

注意使用new重写并没有更改基类的方法。
分部类
值得注意的是,修饰符partial并不是C#关键字,因此在上下文中可以使用partial作为标识符,但如果是在关键字class、struct或interface中,它就代表分部类。
file1.cs
partial class MyPartClass
{
public void Output1(int val)
{
Console.WriteLine("{0}", val);
}
}
file2.cs
partial class MyPartClass
{
public void Output2(int val)
{
Console.WriteLine("{0}", val);
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
MyPartClass myClass = new MyPartClass();
myClass.Output1(22);
myClass.Output2(23);
}
}
is
C#中允许数据在继承链中向下转型,在转换前需要判断数据的类型,可以使用is来判断基础类型。可以这样来理解:若该对象可以非空,且可以强制转换为所提供的类型而不引发异常,则is表达式返回true。使用语法为:
if(obj is objType)
{
}
若obj为null则返回false;
/// <summary>
/// 判断是否为string
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static bool IsString(object data)
{
bool result = false;
if (data is string)
result = true;return result;
}
在上述示例中,实际上CLR会对data进行两次类型检查:首先检查data所引用的对象是否和string兼容;若兼容则有if语句内CLR在执行转换时又会检查data是否为string类型的引用。
object dat1="String";
bool result1 = IsString(dat1);
object dat2 = 23;
bool result2 = IsString(dat2);
object dat3 = null;//空数据
bool result3 = IsString(dat3);
Console.WriteLine(result1.ToString());
Console.WriteLine(result2.ToString());
Console.WriteLine(result3.ToString());
运行结果为

as
在之前一节的类型转换提到了各种类型转换,还有一种转换方式使用as运算符进行转换,as将对象转换为一个特定的数据类型。若转换失败,as运算符会将null值赋给目标,这样就避免了可能因为转型而造成的异常。在msdn有as 的一个经典示例。
class Class1
{
}class Class2
{
}class MainClass
{
static void Main()
{
object[] objArray = new object[6];
objArray[0] = new Class1();
objArray[1] = new Class2();
objArray[2] = "hello";
objArray[3] = 123;
objArray[4] = 123.4;
objArray[5] = null;for (int i = 0; i < objArray.Length; ++i)
{
string s = objArray[i] as string;
Console.Write("{0}:", i);
if (s != null)
{
Console.WriteLine("'" + s + "'");
}
else
{
Console.WriteLine("not a string");
}
}
}
}
运行结果:
