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

Dan7el picture Dan7el · Mar 11, 2011 · Viewed 8.8k times · Source

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 NullReferenceException. 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.

Answer

John Gietzen picture John Gietzen · Mar 11, 2011

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