解析C#,找到方法并将try / catch放到所有方法中

我知道这听起来很奇怪但是我需要在每个方法中放置一个包装try catch块来捕获所有exception。 我们有数千种方法,我需要以自动方式完成。 你有什么建议?

我打算解析所有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/

我必须做一些类似的事情(在很多行代码中添加一些东西); 我用了正则表达式。

我会创建一个正则表达式脚本,找到每个函数的开头,并在开始后插入try catch块。 然后我会创建另一个正则表达式脚本来查找函数的结尾(通过在它之后找到函数的开头)并在结尾处插入try catch块。 这不会让你100%的方式,但它会达到80%,这有望足够接近你可以做边缘情况而不需要太多的工作。

不要做。 口袋妖怪(“必须全部捕获”)error handling没有充分的理由。

编辑:几年后,稍作修改。 我会说“至少不要手动做”。 使用AOP工具或Weaver(如PostSharp或Fody)将其应用于最终结果代码,但请务必考虑其他有用的跟踪或诊断数据点,如捕获执行时间,输入参数,输出参数等。

好吧,如果你必须这样做,那么你必须。 但是,您可能会尝试与强迫您执行此操作的人交谈,以允许您使用AppDomain类的UnhandledException事件 。 在向用户报告之前,它会向您提供有关任何方法中每个未捕获exception的通知。 由于您还可以从exception对象获取堆栈跟踪,因此您将能够准确地确定每个exception发生的位置。 它比使用exception处理程序assembly代码要好得多。

话虽如此,如果我必须这样做,我会使用一些正则表达式来识别每个方法的开始和结束,并使用它来在任何地方插入一些exception处理程序。 为此案例编写正则表达式的技巧将在此处的MSDN文档中详细说明Balancing Group Definition。 此处还有一个使用平衡组的相关示例。

也许谁提出这个要求并不理解你仍然可以捕获所有exception(在顶部)而不在每个函数中都放置try-catch。 您可以在此处查看如何捕获所有未处理的exception的示例。 我认为这是一个更好的解决方案,因为你实际上可以做一些exception,并报告它,而不是盲目地掩盖所有exception,导致极难追踪错误。

这类似于Scott的解决方案,但也为Application.ThreadExceptionexception添加了一个事件处理程序,如果您使用的是线程,则会发生这种情况。 可能最好使用两者来捕获所有exception。

请参阅我的回答,其中描述了如果您使用“全部捕获所有”exception处理,您将被迫忍受的一些性能权衡。

正如斯科特所说,做同样事情的最好方法是UnhandledException事件。 我认为杰夫在早期的SO播客中实际上讨论了这个问题。

我和StingyJack在一起,不要这样做!

然而,如果众神必须要做高法令,那么请看我对这个问题的回答 从cs文件中获取方法的内容

首先,我和StingyJack和Binary Worrier在一起。 有一个很好的理由,默认情况下不会捕获exception。 如果你真的想要捕获exception并且稍微好一点,你可以在Application.Run()调用周围放置一个try-catch块并从那里开始工作。

当处理外部源(文件,因特网等)时,应该(通常)捕获某些exception(连接错误,丢失文件,等等)。 然而,在我的书中,其他任何地方的例外都意味着:1)错误,2)有缺陷的逻辑,或者3)数据validation不良……

总之,完全没有回答你的问题,你确定要这样做吗?

我想写这个作为所有答案的答案然后你可以通过问题RSS知道; 要求来自我们的技术负责人:这是他的理由:我们不需要找出哪个func在生产代码中有问题,任何问题都被报告为警报,我们将一个唯一的代码放到ecery catch块中我们看到问题出在哪里是。 他知道有一个全局error handling,但在这种情况下没有帮助,并且stacktrace在发布模式下不适用,所以他需要一个try catch块,如下所示:

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

如果在每个方法中都放置了一个RaiseAlert调用,那么假设您重用方法,那么您收到的错误堆栈将会非常混乱,如果不是不准确的话。 记录方法实际上只需要在事件或最顶层的方法中调用。 如果某人正在推动exception处理需要在每个方法中的问题,他们就不会理解exception处理。

几年前,我们实施了一种做法,即必须在每个事件中进行exception处理,并且一个开发人员将其视为“每种方法”。 当它们完成后,我们有几个星期值得撤消,因为没有任何exception报告是可以重现的。 我假设他们比你知道的更好,但从未质疑他们解释的有效性。

实现AppDomain.UnhandledException是一个很好的备份,但是你在该方法中唯一的办法是在记录exception后终止应用程序。 您必须编写一个全局exception处理程序来防止这种情况。

所以这里有一个想知道的例子; 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://www.postsharp.org/aopnet/overview

虽然这种要求确实是邪恶的。

如果你真的必须这样做,当你可以直接修改编译的可执行文件/库时,为什么还要修改源代码

看看Cecil(参见网站 ),它是一个允许你直接修改字节码的库,使用这种方法,你的整个问题可以在几百行C#代码中解决。

由于您在此处发布了一个问题,我相信这是您必须要做的事情之一。 因此,为什么不做Scott建议的并使用AppDomain事件处理程序,而不是猛烈地对抗不屈不挠的墙。 您可以满足要求,而无需花费数小时的优质计费时间进行繁重的工作。 我相信一旦你告诉你的老板有多少测试更新每个文件都需要,使用事件处理程序将是一个明智的选择!

所以你并不是真的想在每个函数中放置相同的try-catch块,对吧? 您将不得不为每个函数定制每个try-catch。 哇! 似乎是“简化”调试的漫长道路。

如果用户在生产中报告错误,为什么不能只启动Visual Studio并重现步骤和调试?

如果您必须将try / catch块添加到每个方法并且scott的答案(AppDomain.UnhandledException)是不够的,您还可以查看拦截器。 我相信Castle项目有一个相当不错的方法拦截器实现。

如果你真的必须这样做,每次包装exception的另一种方法是使用Exception.Data来捕获附加信息,然后重新抛出原始exception ……

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

现在,在顶级,您可以转储ex.Data的内容以获取您可能需要的所有其他信息。 这允许您在.Data集合中放置文件名,用户,…以及所有其他有用信息,以帮助理解发生exception的原因。

我做了一些研究工作,需要在大约2年前解析C#代码,并发现SharpDevelop项目的源代码非常好。 如果您从源代码库解压缩SharpDevParser项目(这是两年前,不确定项目名称是否保持不变),那么您可以使用这样的解析器对象:

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

这为您提供了compUnit.Classes,您可以遍历每个类并在其中查找方法。

我正在帮他的朋友在他写的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)