C# .NET错误处理
我一直在编写.NET应用程序,对框架中包含的错误处理印象深刻 当捕捉到进程或代码中某个地方抛出的错误时,我喜欢包括消息(C# .NET错误处理,c#,.net,exception-handling,C#,.net,Exception Handling,我一直在编写.NET应用程序,对框架中包含的错误处理印象深刻 当捕捉到进程或代码中某个地方抛出的错误时,我喜欢包括消息(ex.message,这通常是非常一般的),但也包括stacktrace(ex.stacktrace),这有助于将问题追溯到特定的位置 举个简单的例子,比如说,我们在一个方法中将数字记录到日志中: public void ExampleMethod(int number){ try{ int num = number ...open co
ex.message
,这通常是非常一般的),但也包括stacktrace(ex.stacktrace
),这有助于将问题追溯到特定的位置
举个简单的例子,比如说,我们在一个方法中将数字记录到日志中:
public void ExampleMethod(int number){
try{
int num = number
...open connection to file
...write number to file
}
catch(Exception ex){
.... deal with exception (ex.message,ex.stacktrace etc...)
}
finally{
...close file connection
}
}
是否有任何方法可以查看所调用的方法(在本例中为ExampleMethod
),以及传递的可能导致方法调用崩溃的特定数字?我相信您可以在catch块中记录这一点,但我主要感兴趣的是捕获导致系统抛出异常的方法调用和参数
有什么想法吗?您可以创建一个继承Exception的类,并向其添加一些参数,以便将数字传递给它。如果您想知道方法中参数的值,那么只有一种方法,IMO,可以做到这一点-您需要用数据重新打包异常 例如:
int param1 = 10;
string param2 = "Hello World";
try
{
SomeMethod(param1, param2)
}
catch(SomeExpectedException e)
{
throw new MyParameterSensitiveException(e, param1, param2);
}
基本上,您将原始异常重新打包为另一个异常的内部异常,并另外提供用于调用该方法的参数。然后你可以通过某种方式检查,找出哪里出了问题 我建议将参数值填充到异常的
数据
字典中,例如
public void ExampleMethod(int number) {
try {
int num = number
...open connection to file
...write number to file
}
catch(Exception ex) {
ex.Data["number"] = number;
//.... deal with exception (ex.message,ex.stacktrace etc...)
}
finally {
//...close file connection
}
此方法的另一个优点是,您可以将参数填充到catch
块中,然后重新抛出异常并将其记录到其他地方,而不会丢失堆栈跟踪,例如
catch(Exception ex) {
ex.Data["number"] = number;
throw;
}
为此,请执行以下操作:
public void MyProblematicMethod(int id, string name)
{
try
{
object o = null;
int hash = o.GetHashCode(); // throws NullReferenceException
}
catch (Exception ex)
{
string errorMessage = SummarizeMethodCall(MethodBase.GetCurrentMethod(), id, name);
// TODO: do something with errorMessage
}
}
…然后得到这个:
"MyProblematicMethod invoked: id = 1, name = Charlie"
…您可以这样做:
public static string SummarizeMethodCall(MethodBase method, params object[] values)
{
var output = new StringBuilder(method.Name + " invoked: ");
ParameterInfo[] parameters = method.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
output.AppendFormat("{0} = {1}",
parameters[i].Name,
i >= values.Length ? "<empty>" : values[i]
);
if (i < parameters.Length - 1)
output.Append(", ");
}
return output.ToString();
}
公共静态字符串SummaryMethodCall(MethodBase方法,参数对象[]值)
{
var输出=新的StringBuilder(方法名+“已调用:”);
ParameterInfo[]parameters=method.GetParameters();
对于(int i=0;i=值。长度?“:值[i]
);
如果(i<参数长度-1)
输出。追加(“,”);
}
返回output.ToString();
}
您可以像这样获取方法名称和参数
try
{
int a = 0;
int i = 1 / a;
}
catch (Exception exception)
{
StackTrace s = new StackTrace(exception);
StackFrame stackFrame = s.GetFrame(s.FrameCount - 1);
if (stackFrame != null)
{
StringBuilder stackBuilder = new StringBuilder();
MethodBase method = stackFrame.GetMethod();
stackBuilder.AppendFormat("Method Name = {0}{1}Parameters:{1}", method.Name, Environment.NewLine);
foreach (ParameterInfo parameter in method.GetParameters())
{
stackBuilder.AppendFormat("{0} {1}", parameter.ParameterType.FullName, parameter.Name);
stackBuilder.AppendLine();
}
// or use this to get the value
//stackBuilder.AppendLine("param1 = " + param1);
//stackBuilder.AppendLine("param2 = " + param2);
}
}
我不确定您是否可以像调试器一样直接从堆栈中获取参数值。公认的答案和所描述的许多解决方案都可以很好地工作,但您所做的是根据方法签名中的参数,在源代码中添加一个稍有不同的代码块 当需要添加新参数时,您需要记住更新处理程序以添加新参数。或者,如果删除一个参数,则需要记住从异常处理程序中删除该参数 如果您有两个或更多的
try..catch
块,该怎么办?然后,您现在有两个代码块要保持最新。绝对不是对重构友好的
另一种方法是使用名为的技术删除日志代码
一个这样的工具,以促进这是一个产品称为
使用PostSharp,您可以编写一个记录器,而不需要混乱的方法和特定于参数的代码,只要抛出异常就可以调用它。例如(使用PostSharp的1.5版):
LoggerAttribute.cs-
任何在MyMethod
中引发异常的操作都将导致执行OnException
方法
PostSharp有两个版本。1.5版是GPL下的免费开源版本,面向.NET 2.0。PostSharp 2.0并非完全免费,但其社区版将支持上述基本功能。来自加密模糊器的功能可以满足您的需要
异常报告包括所有相关信息,包括完整堆栈跟踪信息、所有方法参数和局部变量的值,以及系统信息、异常时间、版本号和可选的开发人员定义的自定义数据,如日志文件、屏幕截图等
免责声明:我为LogicNP软件工作,该软件是加密模糊器的开发人员。导致异常的最后一个调用方法已经是堆栈跟踪的一部分。。。你的问题我遗漏了什么吗?@tejs-他也想要传递给方法的参数值。请看这里:我喜欢这种方法。我总是惊讶地发现数据属性很少被使用……哇,我从来没有听说过这个特性一个警告,克里斯蒂安。“不丢失堆栈跟踪”并非100%准确。当您重新抛出异常时,即使使用
throw代码>,则该代码块中的堆栈跟踪位置将重置。因此,如果您正在分发PDB文件以获取堆栈跟踪中的行号,则如果您重试,某些行号将被禁用。该属性提供存储异常详细信息的功能。当系统异常提供必要的实现时,我认为不需要自定义异常。调用记录器(MyLogger.Log(exceptionInstance)
)就足够了。
[Serializable]
public class LoggerAttribute : OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine(eventArgs.Method.DeclaringType.Name);
Console.WriteLine(eventArgs.Method.Name);
Console.WriteLine(eventArgs.Exception.StackTrace);
ParameterInfo[] parameterInfos = eventArgs.Method.GetParameters();
object[] paramValues = eventArgs.GetReadOnlyArgumentArray();
for (int i = 0; i < parameterInfos.Length; i++)
{
Console.WriteLine(parameterInfos[i].Name + "=" + paramValues[i]);
}
eventArgs.FlowBehavior = FlowBehavior.Default;
}
}
[Logger]
public class MyClass
{
public void MyMethod(int x, string name)
{
// Something that throws an exception
}
}