如何确定哪些异常可以由给定方法抛出? [英] How can I determine which exceptions can be thrown by a given method?
问题描述
我的问题是真的一样,因为这有一个<一个href=\"http://stackoverflow.com/questions/264747/finding-out-what-exceptions-a-method-might-throw-in-c\">\"Finding什么异常的方法可能在C#中抛出。不过,我真的想知道是否有人的方法来确定,可能由某一方法抛出的所有异常的堆栈知道。我希望工具或实用程序,我可以在编译时或通过类似的FxCop,了StyleCop,或NCover反射分析code,我不需要在运行时这个信息,我只是想确保我们在正确捕捉异常并记录他们出code。
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.
推荐答案
跟进我的previous的回答,我已经成功地创建一个基本的异常发现者。它利用了基于反射的 ILReader
类,可用<一个href=\"http://blogs.msdn.com/haibo%5Fluo/archive/2006/11/06/system-reflection-based-ilreader.aspx\">here上海博罗的MSDN博客。 (只是一个参考添加到项目中。)
Following up to my previous answer, I've managed to create a basic exception finder. It utilises a reflection-based ILReader
class, available here on Haibo Luo's MSDN blog. (Just add a reference to the project.)
更新:
- 现在,处理局部变量和堆栈。
- 正确地检测从方法调用或字段返回,后来抛出的异常。
- 现在,处理在压入堆栈/充分appropiately弹出。
下面是code,完全。你只是想使用 GetAllExceptions(MethodBase)
方法无论是作为一个扩展或静态方法。
Here is the code, in full. You simply want to use the GetAllExceptions(MethodBase)
method either as an extension or static method.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using ClrTest.Reflection;
public static class ExceptionAnalyser
{
public static ReadOnlyCollection<Type> GetAllExceptions(this MethodBase method)
{
var exceptionTypes = new HashSet<Type>();
var visitedMethods = new HashSet<MethodBase>();
var localVars = new Type[ushort.MaxValue];
var stack = new Stack<Type>();
GetAllExceptions(method, exceptionTypes, visitedMethods, localVars, stack, 0);
return exceptionTypes.ToList().AsReadOnly();
}
public static void GetAllExceptions(MethodBase method, HashSet<Type> exceptionTypes,
HashSet<MethodBase> visitedMethods, Type[] localVars, Stack<Type> stack, int depth)
{
var ilReader = new ILReader(method);
var allInstructions = ilReader.ToArray();
ILInstruction instruction;
for (int i = 0; i < allInstructions.Length; i++)
{
instruction = allInstructions[i];
if (instruction is InlineMethodInstruction)
{
var methodInstruction = (InlineMethodInstruction)instruction;
if (!visitedMethods.Contains(methodInstruction.Method))
{
visitedMethods.Add(methodInstruction.Method);
GetAllExceptions(methodInstruction.Method, exceptionTypes, visitedMethods,
localVars, stack, depth + 1);
}
var curMethod = methodInstruction.Method;
if (curMethod is ConstructorInfo)
stack.Push(((ConstructorInfo)curMethod).DeclaringType);
else if (method is MethodInfo)
stack.Push(((MethodInfo)curMethod).ReturnParameter.ParameterType);
}
else if (instruction is InlineFieldInstruction)
{
var fieldInstruction = (InlineFieldInstruction)instruction;
stack.Push(fieldInstruction.Field.FieldType);
}
else if (instruction is ShortInlineBrTargetInstruction)
{
}
else if (instruction is InlineBrTargetInstruction)
{
}
else
{
switch (instruction.OpCode.Value)
{
// ld*
case 0x06:
stack.Push(localVars[0]);
break;
case 0x07:
stack.Push(localVars[1]);
break;
case 0x08:
stack.Push(localVars[2]);
break;
case 0x09:
stack.Push(localVars[3]);
break;
case 0x11:
{
var index = (ushort)allInstructions[i + 1].OpCode.Value;
stack.Push(localVars[index]);
break;
}
// st*
case 0x0A:
localVars[0] = stack.Pop();
break;
case 0x0B:
localVars[1] = stack.Pop();
break;
case 0x0C:
localVars[2] = stack.Pop();
break;
case 0x0D:
localVars[3] = stack.Pop();
break;
case 0x13:
{
var index = (ushort)allInstructions[i + 1].OpCode.Value;
localVars[index] = stack.Pop();
break;
}
// throw
case 0x7A:
if (stack.Peek() == null)
break;
if (!typeof(Exception).IsAssignableFrom(stack.Peek()))
{
//var ops = allInstructions.Select(f => f.OpCode).ToArray();
//break;
}
exceptionTypes.Add(stack.Pop());
break;
default:
switch (instruction.OpCode.StackBehaviourPop)
{
case StackBehaviour.Pop0:
break;
case StackBehaviour.Pop1:
case StackBehaviour.Popi:
case StackBehaviour.Popref:
case StackBehaviour.Varpop:
stack.Pop();
break;
case StackBehaviour.Pop1_pop1:
case StackBehaviour.Popi_pop1:
case StackBehaviour.Popi_popi:
case StackBehaviour.Popi_popi8:
case StackBehaviour.Popi_popr4:
case StackBehaviour.Popi_popr8:
case StackBehaviour.Popref_pop1:
case StackBehaviour.Popref_popi:
stack.Pop();
stack.Pop();
break;
case StackBehaviour.Popref_popi_pop1:
case StackBehaviour.Popref_popi_popi:
case StackBehaviour.Popref_popi_popi8:
case StackBehaviour.Popref_popi_popr4:
case StackBehaviour.Popref_popi_popr8:
case StackBehaviour.Popref_popi_popref:
stack.Pop();
stack.Pop();
stack.Pop();
break;
}
switch (instruction.OpCode.StackBehaviourPush)
{
case StackBehaviour.Push0:
break;
case StackBehaviour.Push1:
case StackBehaviour.Pushi:
case StackBehaviour.Pushi8:
case StackBehaviour.Pushr4:
case StackBehaviour.Pushr8:
case StackBehaviour.Pushref:
case StackBehaviour.Varpush:
stack.Push(null);
break;
case StackBehaviour.Push1_push1:
stack.Push(null);
stack.Push(null);
break;
}
break;
}
}
}
}
}
要总结,该算法递归枚举(深度优先)的任何调用指定的一个范围内,通过阅读说明书CIL方法(以及跟踪的方法已经访问过)。它维护集合的单独的列表,可以使用 的HashSet&LT; T&GT;
对象,这是在年底返回。它另外保持局部变量和一个堆栈的阵列,以保持该建立之后,不立即抛出的异常的轨道。
To summarise, this algorithm recursively enumerates (depth-first) any methods called within the specified one, by reading the CIL instructions (as well as keeping track of methods already visited). It maintains a single list of collections that can be thrown using a HashSet<T>
object, which is returned at the end. It additionally maintains an array of local variables and a stack, in order to keep track of exceptions that aren't thrown immediately after they are created.
当然,这code在它的当前状态是不可能不犯错。有迹象表明,我需要为它是强大的,一些改进,即:
Of course, this code isn't infallible in it's current state. There are a few improvements that I need to make for it to be robust, namely:
-
检测未使用异常的构造直接抛出的异常。 (即该例外是由一个局部变量或方法调用检索。)秒> -
支持异常弹出堆栈后来被推回。 - 添加流控制检测。该处理任何抛出的异常的try-catch块应该从列表中删除此时,相应的例外,除非检测到
重新抛出
指令。
Detect exceptions that aren't thrown directly using an exception constructor. (i.e. The exception is retrieved from a local variable or a method call.)Support exceptions popped off the stack then later pushed back on.- Add flow-control detection. Try-catch blocks that handle any thrown exception should remove the appropiate exception from the list, unless a
rethrow
instruction is detected.
除此之外,我相信code是的合理的完成。它可能需要更多的调查之前,我弄清楚究竟是如何做到的流量控制检测(虽然我相信我能看看它是如何在IL-级别现在经营)。
Apart from that, I believe the code is reasonably complete. It may take a bit more investigation before I figure out exactly how to do the flow-control detection (though I believe I can see how it operates at the IL-level now).
这些功能也许可以变成整个库如果是要建立一个全功能的异常分析仪,但希望这至少会提供起点这样一个工具声音,如果不是已经足够好它当前的状态。
These functions could probably be turned into an entire library if one was to create a full-featured "exception analyser", but hopefully this will at least provide a sound starting point for such a tool, if not already good enough in its current state.
无论如何,希望帮助!
这篇关于如何确定哪些异常可以由给定方法抛出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!