3

首先,我想我知道发生了什么,但我想我会在这里提出这个问题进行讨论,看看是否有人对此有“答案”,而不是我的想法。因为,这对我来说并不完全有意义。

我发现在为异常创建错误日志时,我正在这样做并且它不起作用:

catch( Exception ex )
{
   LogException( ex.Message );
   if ( !string.IsNullOrEmpty( ex.InnerException.Message ) )
   {
      LogInnerException( ex.InnerException.Message );
   }
}

你瞧,当我运行它时,我经常会得到一个 NullReferenceException。嗯?

我正在检查null,对吗?

现在,我必须使用这个:

   if ( ex.InnerException != null && !string.IsNullOrEmpty( ex.InnerException.Message ) 

但这似乎违反直觉,也适得其反。因为,见鬼,如果我这样做:

   if ( !string.IsNullOrEmpty( null ) )

这根本不会给我带来任何问题。如果 ex.InnerException 为 null,那么 ex.InnerException.Message 肯定为 null,对吗?

显然不是。

我编写了一个完整的控制台应用程序来重现这一点。如果你

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace stringisnullorempty
{
    class Program
    {
        static void Main( string[] args )
        {
            if ( !string.IsNullOrEmpty( null ) )
            {
                Console.WriteLine( "Ha ha ha, right...." );
            }

            MyBClass bClass = new MyBClass();
            bClass.BClassName = "Some B Class Name";
            if ( !string.IsNullOrEmpty( bClass.AClass.AString ) ) //<== Exception occurs here.
            {
                Console.WriteLine( bClass.AClass.AString );
            }
        }
    }

    public class MyAClass
    {
        private string aString;
        public string AString
        {
            get
            {
                return aString;
            }
            set
            {
                aString = value;
            }
        }

        private int aValue;
        public int AValue
        {
            get
            {
                return aValue;
            }
            set
            {
                aValue = value;
            }
        }

        public MyAClass() { }
    }

    public class MyBClass
    {
        private MyAClass aClass;
        public MyAClass AClass
        {
            get
            {
                return aClass;
            }
            set
            {
                aClass = value;
            }
        }

        private string bClassName;
        public string BClassName
        {
            get
            {
                return bClassName;
            }
            set
            {
                bClassName = value;
            }
        }
        public MyBClass() { }
    }
}

我认为正在发生的是代码在尝试处理 IsNullOrEmpty 之前处理 ex.InnerException.Message。由于 ex.InnerException 为空,我们在尝试访问 ex.InnerException.Message 时遇到异常。

我想知道,我需要完整的检查吗?ex.InnerException != null 是否足够。如果我们有一个内部异常,我们是否总是有与之关联的消息?

谢谢。

4

4 回答 4

7

当您调用 时ex.InnerException.Message,它不是 null 的消息,而是InnerException对象。

这样想:

string temp = ex.InnerException.Message
              //               ^ the error is on this dot.
if (string.IsNullOrEmpty(temp))
{
    ...
}

要完全匹配您想要执行的操作,只需使用以下命令:

catch (Exception ex)  // PLEASE catch something more specific.
{
   LogException(ex.Message);
   if (ex.InnerException != null)
   {
      LogInnerException(ex.InnerException.Message);
   }
}

为了解决这个问题,我过去使用过这种方法:

公共异常GetInnermost(异常前)
{
    while (ex.InnerException != null) ex = ex.InnerException;
    返回前;
}

ex.GetBaseException()
于 2011-03-11T13:54:22.327 回答
0

请记住,C# 中的执行顺序是在将参数发送到方法方法体(即,压入堆栈)之前对其进行评估。该指令string.IsNullOrEmpty(x)将首先评估x.

在您的情况下,x示例中是ex.InnerException.Message. 这是从左到右评估的。如果exex.InnerException为空,则抛出 NullReferenceException。

这是解决此问题的一种方法,因为您知道它ex永远不会为空,它将检查是否有 InnerException 的 Message 属性,如果没有 InnerException,则检查 Message 的属性:

if(string.IsNullOrEmpty((ex.InnerException ?? ex).Message))
{
    // .. do something 
}

但是您可能只想首先检查是否有一个InnerException,并且可以这样做:

if(ex.InnerException != null) { ... }

或者您想使用异常或内部异常消息,如果两者都存在则优先使用 InnerException,请使用以下命令:

string exceptionMessage = (ex.InnerException ?? ex).Message;
于 2011-03-11T14:02:48.440 回答
0

基本上,任何时候使用 a. 都有机会出现空引用异常。这很痛苦,但是是的,您需要检查是否InnerException为空。

于 2011-03-11T14:14:01.953 回答
0

如果InnerException为 null 您无法访问其属性之一(Message在您的情况下)。这就是你得到异常的原因,否则不可能。

试着这样想:如何string.IsNullOrEmpty知道你用来传递参数的表达式?对于函数,它只是一个参数。

这两种形式是等价的,但也许第二种对你来说会更清楚:

string.IsNullOrEmpty( ex.InnerException.Message ); // exception here

string test = ex.InnerException.Message; // exception HERE
string.IsNullOrEmpty(test);

希望这很清楚:)

于 2011-03-11T13:58:37.547 回答