How can I determine which exceptions can be thrown

2019-01-08 08:07发布

My question is really the same as this one "Finding out what exceptions a method might throw in C#". However, I would really like to know if anyone knows of a way to determine the stack of all the exceptions that may be thrown by a given method. I am hoping for a tool or utility that I can analyze code at compile time or through reflection like FxCop, StyleCop, or NCover. I do not need this information at run time I just want to make sure we are trapping exceptions and logging them correctly in out code.

We are currently trapping the exceptions that we know about and logging all the wild cards. This does work well; however, i was just hoping someone has used or knows of a tool that can discover this information.

9条回答
等我变得足够好
2楼-- · 2019-01-08 08:46

I'm very doubtful there is any (at least straightforward) way to do this in C#. Saying that, I do have an idea that may work, so read on please...

Firstly, it's worth noting that doing a brute-force search using a huge number of permutations of arguments clearly is not feasible. Even having prior knowledge of parameter types (which I don't believe is desirable in your situation), the task essentially reduces to the halting problem in the general case, since you don't know the function will terminate given certian parameters. Ideally, exceptions should stop this, but it isn't always the case of course.

Now, perhaps the most reliable method is to analyse the source code (or more realistically CIL code) itself to see which exceptions might be thrown. This I believe may actually be workable. A simple algorithm might go something like:

  1. Do a depth-first or breadth-first search of the given method. Find all methods/properties that are called anywhere within the method body, and recurse on them.
  2. For each block of CIL code in tree of methods/propreties, examine code for any exceptions that might be thrown, and add to a list.

This would even allow you to get detailed information about the exceptions, such as whether they are thrown directly by the called method, or deeper in the call stack, or even the exception messages themselves. Anyway, I'll consider giving an implementation of this a try later this afternoon, so I'll let you know how feasible the idea is then.

查看更多
疯言疯语
3楼-- · 2019-01-08 08:50

This should not be extremely hard. You can get list of exceptions created by a method like this:

IEnumerable<TypeReference> GetCreatedExceptions(MethodDefinition method)
{
    return method.GetInstructions()
        .Where(i => i.OpCode == OpCodes.Newobj)
        .Select(i => ((MemberReference) i.Operand).DeclaringType)
        .Where(tr => tr.Name.EndsWith("Exception"))
        .Distinct();
}

Snippets use Lokad.Quality.dll from the Open Source Lokad Shared Libraries (which uses Mono.Cecil to do the heavy-lifting around code reflection). I actually put this code into one of the test cases in trunk.

Say, we have a class like this:

class ExceptionClass
{
    public void Run()
    {
        InnerCall();
        throw new NotSupportedException();
    }

    void InnerCall()
    {
        throw new NotImplementedException();
    }
}

then in order to get all exceptions from just the Run method:

var codebase = new Codebase("Lokad.Quality.Test.dll");
var type = codebase.Find<ExceptionClass>();
var method = type.GetMethods().First(md => md.Name == "Run");

var exceptions = GetCreatedExceptions(method)
    .ToArray();

Assert.AreEqual(1, exceptions.Length);
Assert.AreEqual("NotSupportedException", exceptions[0].Name);

Now all that remains is to walk down the method call stack down to a certain depth. You can get a list of methods referenced by a method like this:

var references = method.GetReferencedMethods();

Now before being able to call GetCreatedExceptions upon any method down the stack we just need to actually look up into the codebase and resolve all MethodReference instances to MethodDefinition instances actually containing the byte code (with some caching to avoid scanning existing branches). That's the most time-consuming part of the code (since Codebase object does not implement any method lookups on top of Cecil), but that should be doable.

查看更多
放我归山
4楼-- · 2019-01-08 08:55

This answer was posted in the other question you reference and I know I have recommended it before in another similar question. You should give Exception Hunter a try. It lists out every single exception that can possibly be thrown. When I ran it for the first time on my code I was quite surprised by the size of this list for even simple functions. There is a 30 day trial for free so there is no reason not to give it a try.

查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-01-08 08:56

John Robbins had a series of articles on creating FxCop rules including an MSDN article that would indicate which exceptions were thrown. This was to warn about missing XML documentation for the exceptions but the idea would be the same.

查看更多
不美不萌又怎样
6楼-- · 2019-01-08 08:57

This isn't so much an answer as building on top of the great work done by @Noldorin above. I used the code above and figured it would be really useful to have a tool that a developer could point at an arbitrary assembly/dll and see the list of exceptions thrown.

By building on top of the work above, I built a tool which does exactly that. I shared the source on GitHub for anybody interested. It's super simple I only had a couple of hours free to write it but feel free to fork and make updates if you see fit...

Exception Reflector on Github

查看更多
SAY GOODBYE
7楼-- · 2019-01-08 08:58

I wrote an add-in for Reflector called ExceptionFinder that handles this. You can get it at:

http://exfinderreflector.codeplex.com/

Regards, Jason

查看更多
登录 后发表回答