先描述問題。異步
最近項目有個需求,數據入庫失敗後延時必定時間而後從新入庫;當失敗達到必定次數後就再也不進行入庫,由於項目簡單,也不須要異步處理。因此看到這個問題很容易想到用遞歸去實現。測試
我最開始的代碼example:spa
/// <summary> /// 錯誤次數 /// </summary> static int errorCount = 0; /// <summary> /// 測試遞歸代碼 /// </summary> /// <returns></returns> static int TestFun() { try { Console.WriteLine("enter fun "); int a = 0; int n = 3 / a; } catch (Exception ex) { if (errorCount >= 3) { Console.WriteLine("number of error==3 bye"); return 0; } errorCount++; Thread.Sleep(1000); TestFun(); } return 1; }
這個代碼我想不少人第一眼看到就很容易想到這個TestFun方法必定返回0,由於除數不能爲0 因此一直報錯 直到錯誤大於3 return了。code
實際結果應該是1 緣由很簡單,catch裏的return 是遞歸這個方法中的return。這個時候TestFun並無所有退出,只是退出了遞歸的那一層而已。遞歸退出完了 也就是catch語句塊執行完畢後,會繼續執行return1。blog
這個問題自己並不難理解,只是咱們都有個固有的思惟 方法中return了 就不會執行下面代碼了。而後就會忽略下面的遞歸調用。遞歸
說到固有思惟我再舉個例子仍是這個問題。it
/// <summary> /// 測試遞歸代碼 /// </summary> /// <returns></returns> static int TestFun() { int result = 1; try { Console.WriteLine("enter fun "); int a = 0; int n = 3 / a; } catch (Exception ex) { if (errorCount >= 3) { Console.WriteLine("number of error==3 bye"); result = 0; return 0; } errorCount++; Thread.Sleep(1000); TestFun(); } return result; }
這個代碼 不直接return具體值了,而是將值保存到一個變量裏。第一眼看這個代碼心想此次應該要返回0了吧,出錯後 result已經被賦值0了 這下最後面的return 應該返回1了。io
正確結果其實也是返回1。說到底仍是由於遞歸,咱們catch 裏的result=0 實際上是針對當前遞歸這個方法裏面的result。由於咱們先遞歸後改變值的;class
咱們能夠這樣去想象:當我一次遞歸時咱們方法是TestFun1 第二次是TestFun2 第三次是 TestFun3 裏面的代碼仍是同樣的。因此我catch裏面的result實際上是對應我TestFun3 裏的result。直到咱們退出遞歸回到最初的方法裏面即TestFun 時它的result仍是1並無改變。變量
形成咱們直覺上的錯誤其實就是我一開始說的那種固有思惟,代碼中變量被賦值後,下面代碼沒有再操做這個值 那麼這個值應該是被修改後的值;固然這種思惟在沒有遞歸代碼當中確定是正確的。
當咱們遞歸寫的少的狀況很容易形成以上那種直覺上的錯誤判斷。