是否有办法找出导致特定原因的{em {em} ?我已经阅读了有关troubleshooting NullReferenceException
s的页面,其中讨论了在调试器中检查变量并查看异常消息。
如果在生产代码中引发了异常,那么您将无法运行调试器来检查变量,该怎么办?异常消息显示了堆栈跟踪,因此您可以查看引发异常的方法,但没有说明哪个特定对象是NullReferenceException
。
我希望能够将null
对象的名称添加到错误消息中,以便在查看用户的报告时遇到null
时,我可以轻松查看NullReferenceException
是什么对象并进行修复。有人知道这样做的方法吗?
我还发现this question提出了同样的问题,但这是从2011年开始的,我不知道此后是否有任何变化。
编辑:The question标记为重复的确实是重复的,但也很旧(2008年)。从那以后有什么改变吗?
编辑2 :我在搜索此问题时发现了this。 Visual Studio可以告诉您引发null
的原因;有什么办法可以将其添加到日志文件中?
答案 0 :(得分:3)
在给定stacktrace的情况下应该相对容易找出,但是更好的方法是在代码中包括“验证”或参数和/或空检查,并在尝试进行操作之前明确地抛出ArgumentNullException
访问可能尚未初始化的变量的成员。然后,您可以提供未初始化对象的名称:
if (obj == null)
throw new ArgumentNullException(nameof(obj));
在构造函数和方法中对参数执行这些检查是一种常见的做法,例如:
public void SomeMethod(SomeType someArgument)
{
if (someArgument == null)
throw new ArgumentNullException(nameof(someArgument));
//you will never get there if someArgument is null...
var someThing = someArgument.SomeMember;
if (someThing == null)
throw new ArgumentException("SomeMember cannot be null.", nameof(someArgument));
...
}
答案 1 :(得分:2)
TL; DR 您的问题的答案是否,不可能。 本文谈论源代码位置而不是对象。但是,您共享的article涵盖了很多答案,如果您完整阅读了该书,您将知道为什么不可能。为了大家的利益,我将在此处添加摘录。
程序集元数据没有调试信息
在运行时期间找到对象的名称要求调试信息可用,该信息基于您用来构建代码的配置。不能保证运行时可以编织地址或注册一个名称。程序集元数据包含对程序集的描述,数据类型和成员及其声明和实现,对其他类型和成员的引用,安全权限,但不包含源信息。
由于您无法控制框架和库(nuget)代码,因此使用PDB会使其不一致
我认为甚至不可能始终如一地做到这一点,即使所有面向CLR的编译器都发出了有关标识符的足够信息(所有语言编译器),并且运行时使用了它。鉴于我可以想到来自社区/ NuGet的参考二进制文件的任何.NET项目,.NET项目的编译方式将不一致。在这种情况下,一部分代码报告标识符名称,而另一部分则不会。
考虑生成的类型(例如IEnumerable)会发生什么情况,运行时可以找出并报告IEnumerable.Current为null,但容器中的基础对象仍然为null,但仍然无法给出答案。您遍历堆栈并找出基础对象并对其进行修复,即使没有信息也是如此。
考虑多线程代码,您可能在其中知道哪个对象为空,但是您可能不知道哪个上下文/调用堆栈导致该对象为空。
所以我的结论是,
我们应该尝试从方法而不是标识符中推断上下文。标识符告诉您什么是空值,但是通常您需要弄清楚为什么它为空值,因为程序员没有预料到它,所以他必须走回栈以找出问题所在。如果对象是局部变量,则这是程序员的错误,可能不必是运行时才能解决。
答案 2 :(得分:1)
每当引发异常时,都会引发AppDomain.CurrentDomain.FirstChanceException
。您可以向此事件添加处理程序,以监视引发了多少异常以及运行时从何处抛出异常。在事件处理程序中,您可以访问实际的Exception
对象。如果需要某种特殊类型的异常,则只需检查传递给处理程序的事件参数对象上的Exception
属性的类型。
以下示例将所有异常(包括内部异常)输出到文本文件(包括堆栈跟踪),以供以后分析。由于通常会捕获并重新抛出异常,因此相同的异常可能在输出文件中多次出现,并且堆栈跟踪越来越长。使用这样的文件,您可以查找特定类型的异常的来源。您还可以从此类文件中获取频率和出现次数以及其他类型的信息。
AppDomain.CurrentDomain.FirstChanceException += (sender, e) =>
{
if (exceptionFile is null)
return;
lock (exceptionFile)
{
if (!exportExceptions || e.Exception.StackTrace.Contains("FirstChanceExceptionEventArgs"))
return;
exceptionFile.WriteLine(new string('-', 80));
exceptionFile.Write("Type: ");
if (e.Exception != null)
exceptionFile.WriteLine(e.Exception.GetType().FullName);
else
exceptionFile.WriteLine("null");
exceptionFile.Write("Time: ");
exceptionFile.WriteLine(DateTime.Now.ToString());
if (e.Exception != null)
{
LinkedList<Exception> Exceptions = new LinkedList<Exception>();
Exceptions.AddLast(e.Exception);
while (Exceptions.First != null)
{
Exception ex = Exceptions.First.Value;
Exceptions.RemoveFirst();
exceptionFile.WriteLine();
exceptionFile.WriteLine(ex.Message);
exceptionFile.WriteLine();
exceptionFile.WriteLine(ex.StackTrace);
exceptionFile.WriteLine();
if (ex is AggregateException ex2)
{
foreach (Exception ex3 in ex2.InnerExceptions)
Exceptions.AddLast(ex3);
}
else if (ex.InnerException != null)
Exceptions.AddLast(ex.InnerException);
}
}
exceptionFile.Flush();
}
};
(示例来自GitHub上的IoT Gateway项目,已获得许可)。