關于exception的基本語法和作用,這里不再贅述,下面記錄一下我在專案中關于Exception的一些思考,
一,使用Exception,而不是Error Code
在初始設計專案的時候,有時候我們為了明確錯誤型別,定義如下結構:
enum ErrCode
{
Ok,
ArgumentErr,
OtherErr1,
OtherErr2
}
class SomeClass
{
public ErrCode DealSomething()
{
//.....
return ErrCode.Ok;
}
}
class CallerClass
{
public void CallSome(SomeClass some)
{
ErrCode err = some.DealSomething();
if(err == ErrCode.Ok)
{
//....
}
else
{
//.....
}
}
}
使用ErrCode并不是說完全不可以,比如在Web Api呼叫中回傳ErrCode就是不錯的選擇,
1,吃掉例外
但是在其他情況,使用ErrCode會讓使用者痛苦不堪,因為每次呼叫都要小心謹慎的處理和判斷ErrCode,否則就特別容易吃掉例外,
對比如下程式片段:
| 使用ErrCode | 使用Exception |
|---|---|
class SomeClass
{
public ErrCode DealSomething()
{
//.....
return ErrCode.SomeErr;
}
}
class CallerClass
{
public void CallSome(SomeClass some)
{
some.DealSomething();
giveMoney();
}
}
|
class SomeClass
{
public void DealSomething()
{
//.....
if(somthingWrong)
{
throw new SomeException(...);
}
}
}
class CallerClass
{
public void CallSome(SomeClass some)
{
some.DealSomething();
giveMoney();
}
}
|
左側使用ErrCode因為不小心沒有接受處理ErrCode,導致后續代碼繼續執行;
而使用Exception,即使沒有處理,也不會執行后續代碼,并且拋出等待上層使用者處理,
此外,Exception還有其他眾多好處,比如Stack Trace資訊,跨process等等,
所以Exception的本質就是現成的錯誤處理(error-handling)機制,就不要再去使用自定義的ErrCode了,
二,Exception在專案中的使用
既然明確使用Exception作為我們的錯誤處理方案,那么具體怎么使用呢?
1,使用細化的Exception
首先要避免以下寫法:
class Dal
{
public void Add(Entity entity)
{
//....
if(somthingWrong)
{
throw new Exception("add wrong");
}
//...
}
}
這種寫法的問題在于,呼叫者無法明確Exception的原因,型別,僅僅依靠檔案或者message去理解,當專案越積越多的時候,就是漫天相同的Exception亂飛,日志里充滿了各種神奇的message的時候,
我們可以采取多維度細化Exception:型別、錯誤種類,具體原因,
使用具體型別化的Exception,比如ConnectionException, TimeoutException, 即為每種錯誤定義一個Exception型別,
這種方法想必大家都很清楚,但是如果一個模塊里有十幾種錯誤,而你又有十幾個模塊呢?
對于這種情況有兩種處理方法,
-
按內部外部劃分
如果一個例外需要模塊外部呼叫者接收和處理的,那么就定義一個具體的Exception型別,比如ConnectionException,TimeoutException;
如果一個例外是模塊記憶體自處理的,那么只用同一個InnerException(或者其他名字)加上其他資訊來區分,(是不是又聞到了ErrCode的味道,沒錯),
這種方法經常在很多開源類別庫中看到,
-
一個模塊定義一種Exception型別,Exception與ErrCode相結合
剛說過ErrCode不能用,這里又提到,大家別誤會,仔細往下看,
比如如下代碼:
class ErrCode { public int Code { get; set; } public string? Name { get; set; } public string? Message { get; set; } } class DalException : Exception { public ErrCode Code { get; } public DalException(ErrCode errCode, Exception? innerException = null):base(errCode.Message, innerException) { Code = errCode; } }ErrCode已經不簡簡單單是一個enum了,而是有更大的用處,
這樣每一個模塊都擁有自己Exception型別,每一種錯誤型別又能得到有效的分類,
這種方法經常在公司專案的業務模塊、基礎框架模塊里用到,
2,使用Exception.Data
上面的代碼段,大家是否有點驚訝,哥們兒,你把Message直接干到ErrCode里面固定起來,是不是有點激進啊,
其實這也是在實際開發中,總結出來的經驗,盡量不要放任程式員們零散的去寫各種Exception的Message,
但是我們可以用Exception.Data來實作更多場景,
比如下面代碼段:
static class ExceptionFactory
{
public static DalException OnMigrateError(int oldVersion, int newVersion, string sql, string cause)
{
DalException ex = new DalException(ErrCodes.MigrationErr);
ex.Data["OldVersion"] = oldVersion;
ex.Data["NewVersion"] = newVersion;
ex.Data["Cause"] = cause;
}
}
class SomeCls
{
void Migration()
{
//.....
throw ExceptionFactory.OnMigrationErr(oldVersion,)
}
}
這樣為每種錯誤固定了具體需要記錄的資訊,其中cause就是讓程式員記錄當下的原因,當然了,你可以重寫OnMigrateError來記錄另外一種場景,
把例外Exception具體需要的資訊,記錄在Data這個Dictionary中, 而不是寫到Message中的好處,是有助于今后進一步的處理,比如結構化日志,
當然這帶來了一個需要關注的問題,就是對Exception.Data的顯式處理,因為Exception.ToString()方法,并不列印Data,
在記錄日志方面,如果你使用 Serilog, 那么推薦你使用 Serilog.Exceptions, 他對Exception.Data十分友好,
至此,我們在細化Exception上,以此遞進的使用了 型別---> ErrorCode ---> 具體cause
3,集中化管理
漫天飛的Exception,不明所以的處理方案,都需要用規則來解決,但是規則需要時刻牢記,所以最靠譜的還是用代碼結構來解決,
首先建立ErrCodes靜態類,然后建立ExceptionFactory類,這樣業務代碼里拋出例外都具有throw ExceptionFactory.XXException()這樣的形式,
//所有的錯誤代碼
internal static class ErrCodes
{
public static ErrCode MigrationErr { get; } = new ErrCode(1, nameof(MigrationErr), "Error happens in Migration.");
//........ other errors
}
//所有的Exception都由此生成
internal static class ExceptionFactory
{
public static DalException OnMigrateError(int oldVersion, int newVersion, string sql, string cause)
{
DalException ex = new DalException(ErrCodes.MigrationErr);
ex.Data["OldVersion"] = oldVersion;
ex.Data["NewVersion"] = newVersion;
ex.Data["Cause"] = cause;
}
//.....其他場景
}
這樣,code review時,只要看到有程式員在代碼中自己 new XXException,那么就督促他在ExceptionFactory里尋找合適的例外場景或者自己添加,
隨著專案積累,即使例外、錯誤種類眾多,大家也只需在ErrCodes和ExcepionFactory兩個類中,總結歸納,重構,
4,參考代碼
具體的代碼,上傳在 Github, 歡迎探討和指正,
三,預告
在下一篇,我將要探討下關于捕捉例外的話題(這么簡單?),
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/273585.html
標籤:.NET Core
