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

时间:2009-01-06 13:57:48

标签: c# parsing

我知道这听起来很奇怪,但是我需要在每个方法中放置一个包装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/

18 个答案:

答案 0 :(得分:40)

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

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

答案 1 :(得分:12)

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

话虽如此,如果我来做,我会使用一些正则表达式来识别每个方法的开始和结束,并使用它来在任何地方插入一些异常处理程序。为这种情况编写正则表达式的技巧将是在MSDN文档here中更多地解释平衡组定义。还有一个使用平衡组here的相关示例。

答案 2 :(得分:3)

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

这类似于Scott的解决方案,但也为Application.ThreadException异常添加了一个事件处理程序,如果您使用的是线程,则会发生这种情况。可能最好同时使用它们以捕获所有异常。

答案 3 :(得分:3)

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

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

答案 4 :(得分:2)

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

然而,如果神在高级法令上必须要做,那么请看answer这个问题Get a method’s contents from a cs file

答案 5 :(得分:2)

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

当处理外部来源(文件,互联网等)时,应该(通常)捕获某些异常(连接错误,丢失文件,等等)。然而,在我的书中,其他地方的例外意味着:1)错误,2)有缺陷的逻辑,或者3)数据验证不良......

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

答案 6 :(得分:1)

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

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

答案 7 :(得分: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);
            }
        }

答案 8 :(得分:1)

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

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

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

答案 9 :(得分:1)

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

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

答案 10 :(得分:0)

如果您真的必须这样做,为什么要在修改源代码时遇到麻烦,何时可以直接修改已编译的可执行文件/库

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

答案 11 :(得分:0)

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

答案 12 :(得分:0)

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

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

答案 13 :(得分:0)

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

答案 14 :(得分:0)

我猜你可以使用面向方面的编程,我想亲自动手。例如http://www.postsharp.org/aopnet/overview

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

答案 15 :(得分: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集合中放置文件名,用户,...以及各种其他有用信息,以帮助理解发生异常的原因。

答案 16 :(得分:0)

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

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

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

答案 17 :(得分:0)

我正在帮他的朋友在他写的C#XNA游戏中发现内存泄漏。 我建议我们尝试检查每个方法被调用的次数。 为了保持计数,我编写了一个python脚本,添加了两行来更新带有详细信息的Dictionary。

基本上我写了一个python脚本来修改一些带有2行所需的400~方法。 这段代码可以帮助别人做更多事情,比如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)