为什么变量不为空时仍显示NullReferenceException?

7

NRE

为什么VS 2012在显示Type变量的同时也显示它的value = "Retailer",却将其显示为空引用异常。

enter image description here

我有一个新生儿,睡眠时间很少,如果我漏看了一些明显的东西,我很抱歉。LoggedInUser.Employer对象已被实例化,这行代码有一半的时间运行良好。但之后就会出问题。不确定这是否有帮助-需要睡觉...

 private string _type;
    public string Type
    {
        get { return _type; }
        set
        {
            if (value != null)
            {
                TypeEnum = (Constants.BusinessType)Enum.Parse(typeof(Constants.BusinessType), value, true);
                _type = value;
            }
        }
    }

enter image description here我开始怀疑这是一个跨线程问题...

enter image description here


4
你能提供每个相关部分的代码吗?比如Employer类中的Type属性? - Simon Whitehead
1
你可能没有正确初始化 LoggedinUser,但是如果没有提供相关代码,就无法确定。如果在 if 语句中转换类型,这会有帮助吗?if(string.IsNullOrEmpty((string)LoggedInUser.Employer.Type))){ } - MethodMan
2
你能展示一些真实的代码而不是模糊的截图吗?根据你所给的内容,我们的调试能力非常有限。 - Simon Whitehead
1
@JeffBorden - 贴出堆栈跟踪! - Parimal Raj
1
“我开始怀疑这可能是跨线程问题”,这可以解释为什么调试器能够正常查看变量。LoggedInUser及其属性如何设置?它是否完全依赖于HttpContext?(HttpContext无法在线程上下文中流动)。 - Snixtor
显示剩余19条评论
3个回答

2
ASP.NET的ExecutionContext负责存储HttpContext.Current实例,它不会自然地“流动”到其他线程。根据您的错误堆栈跟踪,您正在使用ASP.NET MVC,这是一个抽象出HttpContext使用的框架。您可能来自WebForms背景,在那里直接使用它很常见?

SynchronizationContext

本文提供了比我能够合理介绍的更多细节。其中对您情况最相关的一些观点是: "ExecutionContext完全与“环境”信息有关,这意味着它存储与您运行的当前环境或“上下文”相关的数据。" 这个“环境”信息是...HttpContext.Current及其各种属性(包括Session)。 "这意味着我们一直依赖于用于控制执行细节的这个环境上下文已不再可行,因为TLS不会在这些异步点之间“流动”。" TLS是线程局部存储(HttpContext.Current等)。简而言之,异步=可能丢失HttpContext.Current

MVC方式

还记得我说MVC大部分抽象掉了HttpContext吗? SessionController.Session中。(很抱歉,我尚未在异步控制器操作中测试过它,因此无法验证其是否适合您的需求,或者是否需要其他工作来使其配合。) RequestController.Request中。 UserController.User中。
还有其他的...查看它们

会话替代方案?

您考虑过替代方案吗?您不必远走高飞才能找到建议Session + ASP.NET MVC是个坏主意的文章。我不会对像“好事”还是“坏事”这样的泛化问题发表意见,但是看着您的示例,我觉得您正在处理用户配置文件数据,而不是“会话”数据。

会话并不是缓存用户个人资料信息的正确位置。此外,缓存它是否合适呢?用户的个人资料信息在会话期间可能会发生变化吗?如果他们自己更改了它,您会重置会话吗?如果另一个管理员用户在他们登录时更改了他们的个人资料怎么办?

探索替代方案超出了本问题的范围,但请注意您可能正在尝试解决错误的问题。


感谢您编译这个周到的答案。您的假设是正确的,我有WebForms背景,这是我第一次尝试MVC应用程序。 - Jeff Borden
LoggedInUser类存储在会话中,因为其属性在应用程序的整个生命周期中经常被访问。我为什么要这样做?首先,因为这是我所知道的(不是一个很好的理由,但确实是一个理由)。其次,我想避免每次需要该类时都要访问数据库。我考虑将其存储在像memcache这样的缓存中,但没有这样做。如果他们自己更改了数据,那么我会相应地更新会话数据。如果他们的个人资料信息被外部来源(管理员)更改,我没有很好的答案来回答会发生什么。 - Jeff Borden
我还没有谷歌过,但如果你有任何学习更好方法的好链接,请分享。谢谢。 - Jeff Borden
1
一个常见的模式是使用ASP.NET配置文件提供程序 - http://msdn.microsoft.com/en-us/library/014bec1k(v=vs.100).aspx - 你可能会发现这个框架有点“沉重”(只是一个预警)。关于“每次访问数据库”的问题,这是一个权衡。缓存时间过长会导致数据不准确,没有缓存则可能会增加负载。但是,你是否真的有负载问题呢?不要为不存在的问题进行预优化。这是超出本评论讨论范围的问题,所以去搜索一下吧,如果需要的话提出一个新问题。 - Snixtor

1

即使字符串为null,String.IsNullOrEmpty不会在字符串上抛出此异常,因此Type属性不是问题。 LoggedInUser在2行之前被使用而没有错误,因此除非String不是内置的String,否则雇主属性是罪魁祸首。

您可以添加一个检查来确认其是否为空:

if (LoggedInUser.Employer != null) 
{
    if (String.IsNullOrEmpty(LoggedInUser.Employer.Type))
    {
        ...  
    }
}
else
{
    // debug output
}

假设雇主为空,您需要在此处提供该属性的定义。因为只有在多个用户登录时才会看到这个问题,我怀疑某个静态声明出现在不应该的地方。

0
我的猜测是你在某处声明了一个名为“String”的字段 - 尝试使用小写的“string.IsNullOrEmpty”或“System.String.IsNullOrEmpty”。

如果那是他的错误,那么它不会编译,而不是抛出 NRE。 - Servy
@Servy 如果你在谈论类型或变量,C#编译器会自动判断,所以它可以编译通过。但是,除非该字段具有非常奇怪的类型,否则它仍然不会抛出NRE异常。 - CodesInChaos
@CodesInChaos 不,在C#中调用类型实例上的静态方法会生成此错误:““无法使用实例引用访问成员'string.IsNullOrEmpty(string)';请改用类型名称限定它””。这与JAVA不同,因为在JAVA中从类型实例调用静态方法是有效的。 - Servy
@Servy 试一下,它能正常工作。可能是因为(只要你有适当的“using”),“String”既指类型又指字段。只有在尝试调用非“String”命名的字符串字段上的“IsNullOrEmpty”时才会出现该错误。 - CodesInChaos
@CodesInChaos 那样做就可以了,而且是否有一个名为“String”的变量也无关紧要,因为它不能掩盖它。重点是这不是OP的问题。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接