2009-01-06 30 views
1

我知道這聽起來很奇怪,但我需要爲每個方法放置一個包裝try catch塊來捕獲所有異常。我們有成千上萬的方法,我需要以自動化的方式來完成。你有什麼建議?解析C#,找到方法並將try/catch放到所有方法

我打算解析所有的cs文件並檢測方法並在應用程序中插入try catch塊。你能向我推薦任何我可以輕鬆使用的解析器嗎?或任何會幫我...

每一個方法都有其像5006

public static LogEntry Authenticate(....) 
     { 
      LogEntry logEntry = null; 
      try 
      { 
       .... 
       return logEntry; 
      } 

      catch (CompanyException) 
      { 
       throw; 
      } 

      catch (Exception ex) 
      { 
       logEntry = new LogEntry(
        "5006", 
        RC.GetString("5006"), EventLogEntryType.Error, 
        LogEntryCategory.Foo); 

       throw new CompanyException(logEntry, ex); 
      } 
     } 

我創造了這個這個唯一的號碼; http://thinkoutofthenet.com/index.php/2009/01/12/batch-code-method-manipulation/

+0

沒有這一要求來自哪裏? – 2009-01-06 14:01:06

+0

我想想,把一個try/catch是不要做的事情,但真正的aswer在這裏使用正則表達式..這就是爲什麼真正的答案是不同的比,如果有什麼在你的catch塊,如發生異常的最投票 – erdogany 2009-01-13 15:40:27

+1

在RC.GetString()?爲了以防萬一,你不應該再試一試嗎?通過閱讀問題[開玩笑] ;-) – 2010-04-12 05:46:30

回答

1

我必須做一些事情有點八九不離十類似(添加的東西很多行代碼);我用正則表達式。

我會創建一個正則表達式腳本,它可以找到每個函數的開頭,並在開始之後插入try catch塊。然後,我會創建另一個正則表達式腳本來查找函數的結尾(通過查找函數的開頭),並在末尾插入try catch塊。這不會讓你百分之百的方式,但它可能會達到80%,這將希望足夠接近,你可以做邊緣情況下沒有太多的工作。

40

不要做。口袋妖怪沒有很好的理由(「抓住所有人」)錯誤處理。

編輯:幾年後,略有修改是爲了。我會說,而不是「至少不要手動」。使用AOP工具或編織器(如PostSharp或Fody)將其應用於最終結果代碼,但要確保考慮其他有用的跟蹤或診斷數據點,如捕獲執行時間,輸入參數,輸出參數等。

+0

但願如此..可怕的問題:) – 2009-01-06 14:00:41

+0

中途,所有打算通過我的頭「NOooooooooooooooo」 – marcumka 2009-01-06 14:13:10

+0

這不是我的決定,大家好,感謝反正:) – erdogany 2009-01-06 14:22:53

12

那麼,如果你必須這樣做,那麼你必須。然而,你可能會試圖說出誰強迫你這樣做,讓你使用UnhandledException event of the AppDomain class。在向用戶報告之前,它會通知您有關任何方法中的每個未捕獲異常。由於您也可以從異常對象中獲取堆棧跟蹤,因此您可以準確地確定發生異常的位置。這是一個更好的解決方案,而不是遍佈各處的異常處理程序來安裝代碼。

這樣說,如果我這樣做,我會使用一些正則表達式來確定每個方法的開始和結束,並用它來插入一些異常處理程序。爲這種情況編寫正則表達式的技巧將是在MSDN文檔here中更多地解釋「平衡組定義」。還有一個使用平衡組here的相關示例。

2

首先,我與StingyJack和Binary Worrier一起。有一個很好的理由,例外不是默認捕獲的。如果你真的想要捕捉異常並稍微好一點,你可以在Application.Run()調用周圍放一個try-catch塊並從那裏開始工作。

當處理外部來源(文件,互聯網等)時,應該(通常)捕捉某些異常(錯誤連接,丟失文件,等等)。然而,在我的書中,任何其他地方的例外都意味着1)錯誤,2)有缺陷的邏輯,或3)數據驗證不良......

總之,要完全不回答你的問題,你肯定是你想這樣做?

3

也許誰想出了這個要求不明白,你仍然可以趕上(頂部)的所有異常沒有把一個try-catch在每一個功能。你可以看到如何catch all unhandled exceptions here的例子。我認爲這是一個更好的解決方案,因爲您可以對異常做些實際的事情並報告,而不是盲目地掩蓋所有異常情況,導致很難追蹤錯誤。

這與斯科特的解決方案,而且還增加了一個事件處理程序Application.ThreadException例外,它可以,如果你正在使用的線程發生。可能最好使用這兩種方法來捕捉所有例外情況。

0

如果你真的必須這樣做,爲什麼還要去修改源代碼時,你可以修改編譯的可執行文件/庫直接

看一看塞西爾(見website)的麻煩,這是一個允許你直接修改字節碼的庫,使用這種方法,你的整個問題可以在幾百行C#代碼中解決。

3

查看我的回答here其中描述了一些性能權衡問題,如果您使用「必須全部處理」異常處理,您將被迫與之共處。

斯科特說,幾乎做同樣的事情,最好的辦法是UnhandledException事件。我認爲傑夫在早期的SO播客中實際上討論過這個問題。

1

我想寫這個作爲所有答案的答案,然後你可以通過問題RSS意識到; 需求來自我們的技術領導者: 這裏是他的理由: 我們不需要找出FUNC在生產代碼中的問題,任何問題已經被報告爲警告,我們把一個唯一代碼ecery catch塊,我們看到問題在哪裏。他知道有一個全球性的錯誤處理,但它不能在這種情況下幫助和堆棧跟蹤不釋放模式accure,所以他需要一個try catch塊這樣的:

everyMethod(...){ 
    try 
    { 
    .. 
    catch(Exception e) 
    { 
     RaiseAlert(e.Message.. blabla) 
    } 
} 
0

既然你在這裏發帖提問,我相信這是你必須做的事情之一。因此,爲了不讓你的頭撞到堅硬的牆上,爲什麼不做Scott建議並使用AppDomain事件處理函數。無需花費數小時的質量計費工作就可以滿足要求。我相信,一旦你告訴你的老闆多少測試更新每一個文件將需要,使用事件處理程序將是一件容易的事!

0

所以你並不是真的想把每個函數中相同的try-catch塊放在一起,對吧?你將不得不爲每個功能定製每個try-catch。哇!看起來像「簡化」調試的很長一段路。

如果用戶報告在生產中的錯誤,爲什麼你就不能火了Visual Studio和複製的步驟和調試?

0

如果您絕對必須將try/catch塊添加到每個方法,並且scott的答案(AppDomain.UnhandledException)不夠用,您還可以查看攔截器。我相信Castle項目對方法攔截器有相當好的實現。

1

如果您在每種方法中都使用RaiseAlert調用,那麼假設您重用方法,則您收到的錯誤堆棧將非常混亂,如果不是不準確的話。日誌記錄方法應該只需要在事件或最上面的方法中調用。如果有人推動異常處理需要在每個方法中出現的問題,他們不理解異常處理。

幾年前,我們實施了一項做法,即必須在每個事件中完成異常處理,並且一名開發人員將其讀作「每種方法」。當它們完成時,我們有幾周的時間值得撤銷,因爲沒有任何異常報道可以重現。 我假設他們知道更好,就像你一樣,但從來沒有質疑他們解釋的有效性。

實現AppDomain.UnhandledException是一個很好的備份,但是您唯一的方法是在您記錄異常後終止應用程序。你必須編寫一個全局異常處理程序來防止這種情況。

1

所以這裏是一個例子,爲那些想知道的; 5006是這種方法所特有的;

public static LogEntry Authenticate(....) 
     { 
      LogEntry logEntry = null; 
      try 
      { 
       .... 
       return logEntry; 
      } 

      catch (CompanyException) 
      { 
       throw; 
      } 

      catch (Exception ex) 
      { 
       logEntry = new LogEntry(
        "5006", 
        RC.GetString("5006"), EventLogEntryType.Error, 
        LogEntryCategory.Foo); 

       throw new CompanyException(logEntry, ex); 
      } 
     } 
0

如果你真的必須這樣做,每次包裝異常的替代辦法是使用Exception.Data捕捉更多的信息,然後重新拋出原始異常 ...

catch (Exception ex) 
{ 
    logEntry = new LogEntry("5006", 
        RC.GetString("5006"), EventLogEntryType.Error, 
        LogEntryCategory.Foo); 
    ex.Data.Add("5006",logEntry); 
    throw; 
} 

現在在頂層,您可以轉儲ex.Data的內容以獲取所有您可能需要的附加信息。這使您可以在.Data集合中放置文件名,useragents ...以及所有其他有用信息,以幫助理解發生異常的原因。

0

我做了一些研究工作,需要大約2年前解析C#代碼,並發現SharpDevelop項目具有很好的源代碼。如果提取SharpDevParser項目(這是兩年前,不知道項目名稱保持不變),從源代碼庫,然後你可以使用的解析器對象是這樣的:

CSharpBinding.Parser.TParser = new CSharpBinding.Parser.TParser(); 
SIP.ICompilationUnitBase unitBase = sharpParser.Parse(fileName); 

這給你的compUnit.Classes,您可以遍歷每個類並在其中找到方法。

0

我正在幫朋友在他寫的C#XNA遊戲中發現內存泄漏。 我建議我們試着檢查每個方法被調用的次數。 爲了保持計數,我寫了一個python腳本,增加了2行來更新一個Dictionary的細節。

基本上我寫了一個python腳本來修改一些400〜2個必需的行的方法。 這段代碼可能會幫助別人做更多的事情,比如OP想要的東西。

該代碼使用第3行配置的路徑,並在處理.cs文件時遞歸迭代。它也有子目錄。

當它找到一個cs文件時,它會查找方法聲明,它會盡可能地小心。做一個備份 - 如果我的腳本違反了你的代碼,我不負責任!

import os, re 

path="D:/Downloads/Dropbox/My Dropbox/EitamTool/NetworkSharedObjectModel" 
files = [] 

def processDir(path, files): 
    dirList=os.listdir(path) 
    for fname in dirList: 
     newPath = os.path.normpath(path + os.sep + fname) 
     if os.path.isdir(newPath): 
      processDir(newPath, files) 
     else: 
      if not newPath in files: 
       files.append(newPath) 
       newFile = handleFile(newPath) 
      if newPath.endswith(".cs"): 
       writeFile(newPath, newFile) 

def writeFile(path, newFile): 
    f = open(path, 'w') 
    f.writelines(newFile) 
    f.close() 

def handleFile(path): 
    out = [] 
    if path.endswith(".cs"): 
     f = open(path, 'r') 
     data = f.readlines() 
     f.close() 

     inMethod = False 
     methodName = "" 
     namespace = "NotFound" 
     lookingForMethodDeclerationEnd = False 
     for line in data: 
      out.append(line) 
      if lookingForMethodDeclerationEnd: 
       strippedLine = line.strip() 
       if strippedLine.find(")"): 
        lookingForMethodDeclerationEnd = False 
      if line.find("namespace") > -1: 
       namespace = line.split(" ")[1][0:-2] 
      if not inMethod: 
       strippedLine = line.strip() 
       if isMethod(strippedLine): 
        inMethod = True 
        if strippedLine.find(")") == -1: 
         lookingForMethodDeclerationEnd = True 
        previousLine = line 
      else: 
       strippedLine = line.strip() 
       if strippedLine == "{": 
        methodName = getMethodName(previousLine) 
        out.append('   if (!MethodAccess.MethodAccess.Counter.ContainsKey("' + namespace + '.' + methodName + '")) {MethodAccess.MethodAccess.Counter.Add("' + namespace + '.' + methodName + '", 0);}') 
        out.append("\n" + getCodeToInsert(namespace + "." + methodName)) 
        inMethod = False 
    return out 

def getMethodName(line): 
    line = line.strip() 
    lines = line.split(" ") 
    for littleLine in lines: 
     index = littleLine.find("(") 
     if index > -1: 
      return littleLine[0:index] 


def getCodeToInsert(methodName): 
    retVal = "   MethodAccess.MethodAccess.Counter[\"" + methodName + "\"]++;\n" 
    return retVal 

def isMethod(line): 
    if line.find("=") > -1 or line.find(";") > -1 or line.find(" class ") > -1: 
     return False 
    if not (line.find("(") > -1): 
     return False 
    if line.find("{ }") > -1: 
     return False 
    goOn = False 
    if line.startswith("private "): 
     line = line[8:] 
     goOn = True 
    if line.startswith("public "): 
     line = line[7:] 
     goOn = True 
    if goOn: 
     return True 
    return False 

processDir(path, files)