0

I was getting a NullReferenceException in a web application and spent a huge deal of time to actually find the problem. I reproduced the problem with a console application. You can try to run the following code as it is –

using System;
using System.Linq.Expressions;

namespace Expression
{
    public struct ValueType { }
    public class ReferenceType { }
    public class MyClass
    {
        public ReferenceType ReferenceType { get; set; }
        public ValueType ValueType { get; set; }
    }

    class Program
    {
        public static string GetPropertyName<T>(Expression<Func<T, object>> expression)
        {
            return (expression.Body as MemberExpression).Member.Name;
        }
        static void Main(string[] args)
        {
            MyClass c1 = new MyClass();
            MyClass c2 = new MyClass();

            Console.WriteLine(GetPropertyName<MyClass>(x => x.ReferenceType));
                // No Error


            Console.WriteLine(GetPropertyName<MyClass>(x => x.ValueType)); 
                // System.NullReferenceException
        }
    }
} 

So the problem is the generic function GetPropertyName works when the expression given as the parameter of the function with the Reference Type property but The Value Type property causes System.NullReferenceException at (expression.Body as MemberExpression).Member.

So my question is why it works for reference type and not value type?

Amit Hasan
  • 1,230
  • 1
  • 14
  • 30
  • I believe this can happen if "expression.Body" isn't of type "MemeberExpression." – Ranger May 27 '16 at 21:19
  • @NexTerren it is a `UnarayExpression`, the question is, why? – Scott Chamberlain May 27 '16 at 21:20
  • 1
    @JonathonReinhart I think it's a bit different. – Amit Hasan May 27 '16 at 21:21
  • 2
    See: http://stackoverflow.com/a/3573250/15541 for why – leppie May 27 '16 at 21:22
  • @amit at the moment its not. Once you do basic debugging of the problem and find the root cause of the NRE you could potentially turn it into a different question. – Servy May 27 '16 at 21:24
  • @ScottChamberlain: That answers the question (key being boxing), but the way it is phrased, it seems the OP does not know how to debug, else it would be obvious. – leppie May 27 '16 at 21:25
  • @leppie you are right. – Amit Hasan May 27 '16 at 21:26
  • 1
    In summary of the duplicate, it is because your expression only accepts `object` as the return type, so the value type must be boxed and this results in a `UnarayExpression` of `Convert(x.ValueType)`, if you checked the `.Operand` of that expression you would find your `MemberExpression` you where looking for (or just let it take in a generic instead of object so it does not need to box). – Scott Chamberlain May 27 '16 at 21:29
  • @ScottChamberlain Thank you very much. – Amit Hasan May 27 '16 at 21:30

2 Answers2

1
(expression.Body as MemberExpression).Member

If expression.Body can't be cast to MemberExpression, then the expression returns null. Trying to access the Member member then of course raises a NullReferenceException.

Jonathon Reinhart
  • 116,671
  • 27
  • 221
  • 298
1

It's easy to see expression.Body as MemberExpression is null for the second call, because expression.Body is of type UnaryExpression and not MemberExpression.

Why? The actual operation is "convert to System.Object". Conversion of a value type to a reference is known as boxing, and boxing is required if the delegate has to return object.

We can illustrate it on the following piece of CIL, which can be represented by the lambdas:

//x => x.ReferenceType
ldarg.0
callvirt instance class ReferenceType MyClass::get_ReferenceType()
ret

 

//x => x.ValueType
ldarg.0
callvirt instance class ReferenceType MyClass::get_ReferenceType()
box
ret

As you can see, the second function contains an additional box instruction doing what I've described above. The generated expression is similar to the CIL in the "convert" operation, essentially doing the boxing. If you try to create the same expression but without the conversion, it throws an exception.

IS4
  • 9,770
  • 2
  • 41
  • 69