【问题标题】:An interesting c# NullReferenceException [duplicate]一个有趣的 c# NullReferenceException [重复]
【发布时间】:2016-05-27 21:16:41
【问题描述】:

我在一个 Web 应用程序中遇到了 NullReferenceException,并花费了大量时间来实际发现问题。我用控制台应用程序重现了这个问题。您可以尝试按原样运行以下代码-

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
        }
    }
} 

所以问题是通用函数 GetPropertyName 在作为具有引用类型属性的函数的参数给出的表达式但值类型属性导致System.NullReferenceException(expression.Body as MemberExpression).Member 时起作用。

所以我的问题是为什么它适用于引用类型而不是值类型?

【问题讨论】:

  • 我相信如果“expression.Body”的类型不是“MemeberExpression”,就会发生这种情况。
  • @NexTerren 它是UnarayExpression,问题是,为什么?
  • @JonathonReinhart 我觉得有点不一样。
  • 查看:stackoverflow.com/a/3573250/15541 了解原因
  • 总结重复,是因为你的表达式只接受object作为返回类型,所以值类型必须装箱,这会导致Convert(x.ValueType)UnarayExpression,如果你检查了那个表达式的.Operand,你会发现你在寻找你的MemberExpression(或者只是让它接受一个泛型而不是对象,这样它就不需要装箱了)。

标签: c#


【解决方案1】:

很容易看出expression.Body as MemberExpression 在第二次调用中是null,因为expression.Body 的类型是UnaryExpression 而不是MemberExpression

为什么?实际操作是“转换为System.Object”。将值类型转换为引用称为装箱,如果委托必须返回 object,则需要装箱。

我们可以用下面的 CIL 来说明它,它可以用 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

如您所见,第二个函数包含一个附加的box 指令,执行我上面描述的操作。生成的表达式类似于“转换”操作中的 CIL,本质上是在做装箱。如果您尝试创建相同的表达式但没有进行转换,则会引发异常。

【讨论】:

    【解决方案2】:
    (expression.Body as MemberExpression).Member
    

    如果expression.Body 不能转换为MemberExpression,则表达式返回null。尝试访问Member 成员当然会引发NullReferenceException

    【讨论】:

    • 我认为 OP 更好奇(或者至少我是)为什么结构决定成为 UnarayExpression 而不是 MemberExpression
    • 我之前还不清楚,我的问题是为什么它适用于引用类型而不是值类型?
    • @scott 那么他应该问这个而不是他问了什么。
    • @Servy 你是对的。很抱歉给您带来不便。
    • 你的回答是正确的,考虑到我实际上是如何问这个问题的。很抱歉给您带来不便。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-26
    相关资源
    最近更新 更多