开发者

string.IsNullOrEmpty() Doesn't Seem to Work on a String within a Class within a Class

First off, I think I know what's going on, but I thought I'd bring this issue up here for some discussion and see if anyone has an "answer" to this other than what I'm thinking. Because, it doesn't completely make sense to me.

What I found is that when creating a error log for exceptions, I was doing this and it wasn't working:

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

and lo and behold, when I ran this I'd often get a NullRe开发者_StackOverflow中文版ferenceException. Huh?

I'm checking for null, right?

now, I have to use this:

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

but that seems counter-intuitive and also counter productive. Because, heck, if I do this:

   if ( !string.IsNullOrEmpty( null ) )

That doesn't give me any problems at all. And if ex.InnerException is null, then certainly ex.InnerException.Message is null, right?

Apparently not.

I wrote a complete console app that reproduces this. If you

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() { }
    }
}

What I think is happening is that the code processes ex.InnerException.Message before trying to process the IsNullOrEmpty. Since ex.InnerException is null, we get an exception trying to access ex.InnerException.Message.

I'm wondering though, do I need the full check? Will the ex.InnerException != null be enough. If we have an inner exception, will we always have a message associated with it?

Thanks.


When you call ex.InnerException.Message, it's not the message that is null, but rather then InnerException object.

Think of it this way:

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

To match exactly what you want to do, just use this:

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

In order to solve this problem, I have used this method in the past:

public Exception GetInnermost(Exception ex)
{
    while (ex.InnerException != null) ex = ex.InnerException;
    return ex;
}

ex.GetBaseException()


If InnerException is null you cannot access one of its properties (Message in your case). This is why you get the exception, and it could not be otherwise.

Try to think like this: how can string.IsNullOrEmpty know the expression you are using to pass it a parameter? For the function, it is just a parameter.

These two forms are equivalent, but maybe the second will be clearer for you:

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

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

Hope this is clear :)


Remember that the order of execution in C# is that parameters are evaluated before they're send through to the method method body (i.e., pushed on the stack). The instruction string.IsNullOrEmpty(x) will first evaluate x.

In your case, x in the example is ex.InnerException.Message. This is evaluated from left to right. If either ex, or ex.InnerException are null, a NullReferenceException is thrown.

Here's a way to work around this, because you know that ex is never null, which will check the Message property of InnerException if there is any, or of Message if there is no InnerException:

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

But you probably just want to check whether there is an InnerException in the first place and that can be done like so:

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

Or you want to use either the exception or the inner exception message, with preference for InnerException if both are there, use this:

string exceptionMessage = (ex.InnerException ?? ex).Message;


Basically, any time you use a . you've got a chance for a null reference exception. It's a pain, but yeah, you need to check InnerException for null.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜