C#异常处理
参考官方文档异常和异常处理(C# 编程指南)
C# 异常处理时建立在四个关键词之上的:try
、catch
、finally
和 throw
try
:一个try
块标识了一个将被激活的特定的异常的代码块。后跟一个或多个catch
块。catch
:程序通过异常处理程序捕获异常。catch
关键字表示异常的捕获。finally
:finally
块用于执行给定的语句,不管异常是否被抛出都会执行。例如,如果您打开一个文件,不管是否出现异常文件都要被关闭。throw
:当问题出现时,程序抛出一个异常。使用throw
关键字来完成。
异常是最终全都派生自 c 的类型。System.Exception
类有几个非常有用的属性:
StackTrace
-当前调用堆栈上的方法的名称,以及为每个方法引发异常的位置(文件名和行号)Message
-解释发生异常的原因
自定义异常
自定义异常类要继承自System.Exception
, 派生类应至少定义四个构造函数
- 一个默认构造函数
- 一个用于设置消息属性的构造函数
- 一个用于设置
Message
和InnerException
属性的构造函数 - 一个用于序列化异常的构造函数
可参考Part 42 - C# Tutorial - Custom Exceptions
如下:
[Serializable()]
public class InvalidDepartmentException : System.Exception
{
public InvalidDepartmentException() : base() { }
public InvalidDepartmentException(string message) : base(message) { }
public InvalidDepartmentException(string message, System.Exception inner) : base(message, inner) { }
// A constructor is needed for serialization when an
// exception propagates from a remoting server to the client.
protected InvalidDepartmentException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) { }
}
Inner Exceptions
using System;
using System.IO;
class ExceptionHandling
{
public static void Main()
{
try
{
try
{
Console.WriteLine("Enter First Number");
int FN = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter Second Number");
int SN = Convert.ToInt32(Console.ReadLine());
int Result = FN / SN;
Console.WriteLine("Result = {0}", Result);
}
catch (Exception ex)
{
string filePath = @"C:\Sample Files\Log.txt";
if (File.Exists(filePath))
{
StreamWriter sw = new StreamWriter(filePath);
sw.Write(ex.GetType().Name + ex.Message + ex.StackTrace);
sw.Close();
Console.WriteLine("There is a problem! Plese try later");
}
else
{
//把原来的异常作为一个参数传递给当前的exception
throw new FileNotFoundException(filePath + " Does not Exist", ex);
}
}
}
catch (Exception ex)
{
//ex.Message获取当前的异常的信息
Console.WriteLine("Current or Outer Exception = {0}" + ex.Message);
//检查内部的exception是否为null
if(ex.InnerException != null)
{
Console.WriteLine("Inner Exception = {0}", ex.InnerException.Message);
}
}
}
}
如果输入10/0
,控制台输出如下的信息:
Enter First Number
10
Enter Second Number
0
Current or Outer Exception = {0}C:\Sample Files\Log.txt Does not Exist
Inner Exception = 尝试除以零。
catch 块
在下列条件要捕获异常:
1.很好地理解可能会引发异常的原因,并且可以实现特定的恢复,例如当捕获到 FileNotFoundException
对象时提示用户输入新的文件名。
2.可以创建和引发一个新的、更具体的异常
int GetInt(int[] array, int index)
{
try
{
return array[index];
}
catch(System.IndexOutOfRangeException e)
{
throw new System.ArgumentOutOfRangeException(
"Parameter index is out of range.", e);
}
}
3.想要先对异常进行部分处理,然后再将其传递以进行额外处理。 在下面的示例中,catch
块用于在重新引发异常之前将条目添加到错误日志
try
{
// Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
// Call a custom error logging procedure.
LogError(e);
// Re-throw the error.
throw;
}
创建和引发异常
异常用于指示在运行程序时发生了错误。 此时将创建一个描述错误的异常对象,然后使用 throw
关键字引发。 然后,运行时搜索最兼容的异常处理程序。
当存在下列一种或多种情况时,程序员应引发异常:
1.方法无法完成其定义的功能。例如,如果一种方法的参数具有无效的值:
static void CopyObject(SampleClass original)
{
if (original == null)
{
throw new System.ArgumentException("Parameter cannot be null", "original");
}
}
2.根据对象的状态,对某个对象进行不适当的调用。一个示例可能是尝试写入只读文件。 在对象状态不允许操作的情况下,引发 InvalidOperationException
的一个实例或基于此类的派生的对象。 以下为引发 InvalidOperationException
对象的方法的示例:
class ProgramLog
{
System.IO.FileStream logFile = null;
void OpenLog(System.IO.FileInfo fileName, System.IO.FileMode mode) {}
void WriteLog()
{
if (!this.logFile.CanWrite)
{
throw new System.InvalidOperationException("Logfile cannot be read-only");
}
// Else write data to the log and return.
}
}
3.方法的参数引发了异常。在这种情况下,应该捕获原始异常,并创建一个 ArgumentException
实例。 应将原始异常传递给 ArgumentException
的构造函数作为 InnerException
参数:
static int GetValueFromArray(int[] array, int index)
{
try
{
return array[index];
}
catch (System.IndexOutOfRangeException ex)
{
System.ArgumentException argEx = new System.ArgumentException("Index is out of range", "index", ex);
throw argEx;
}
}