使用空操作数的字符串连接运算符

8

我的同事向我展示了一个非常奇怪的行为,我想知道是否有人能够解释一下。

一个具有两个字符串参数的基本构造函数:

    public MyClass(string str1, string str2)
    {
        this.s1 = str1;
        this.s2 = str2;
        this.s3 = Method(str2 + "._className", str1);
    }

方法是:

public string Method(string key, string defaultValue)
{
    List<string> list = _vars[key];
    if (list == null) return defaultValue;
    string res = "";
    foreach (string s in list)
    {
        if (res != "") res += ",";
        res += s;
    }
    return res;
}

当在包含 str2null 的 aspx 页面中调用此构造函数时,一切都可以正常工作,因为如果字符串拼接操作符 + 的操作数为 null,则将替换为空字符串。
但是,当在后台线程中以 str2null 调用此构造函数时,会触发一个 NullReferenceException 异常。
通过在使用之前测试 str2 != null 来解决了这个问题,但我真的很想知道为什么相同的代码有时会引发异常,有时不会!
以下是堆栈跟踪:
Exception: System.NullReferenceException 
Message: Object reference not set to an instance of an object.
StackTrace: 
at MyClass..ctor(String str1, String str2) 
at AbandonedCartsNotificationJob.NotifyAbandonedCarts() in AbandonedCartsNotificationJobPartial.cs:line 39 
at AbandonedCartsNotificationJob.work() in AbandonedCartsNotificationJob.cs:line 15 
at MyRuntime.JobManager.run() 
at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
at System.Threading.ExecutionContext.runTryCode(Object userData) 
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) 
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) 
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
at System.Threading.ThreadHelper.ThreadStart()

17
我感觉这里还有其他的事情发生了。this.s2 有什么用?堆栈跟踪是什么?您能发布可重现的代码供我们测试吗?您在哪里进行空值检查?是在实例化 MyClass 之前、在执行 Method 之前,还是在构造函数开始时? - Chris Sinclair
3
您确定已经将问题追踪到了正确的代码部分吗?Method函数是做什么用的? - Damien_The_Unbeliever
2
@Sylv21:这是开启了调试模式吗?(也就是编译器优化关闭了)就目前而言,我没有看到你的构造函数中有任何可能导致堆栈跟踪的代码。我认为只有通过方法内联(因为进行了发布模式的编译优化)才可能发生这种情况。(除非你没有发布你的构造函数的实际代码) - Chris Sinclair
2
我看到的唯一东西是_vars[key],_vars可能为空,你能检查一下吗? - Swift
2
顺便提一下,请出于健全的考虑,将您的foreach循环替换为适当的String.Join调用 :-) - sloth
显示剩余19条评论
2个回答

4
.NET Framework的字符串拼接实现中存在一个晦涩难懂的bug,但它只会影响包含4个对象的拼接,其中一个对象为非空并提供了返回null的ToString覆盖。显然这种情况在这里不适用。
最可能导致这种情况的原因是以下之一:
- 当调用Method时,_vars为空。 - 在多线程应用程序中误用_vars导致其内部状态受损,在使用[]操作符时导致NullReferenceException。

这确实是_var的问题,Method最初并不是设计用于多线程模式。 - Sylv21

3
问题出在Method对象的实现上。由于+操作符的实现将null值解释为空字符串,当在str2中设置为null值时,实际上该null值从未进入构造函数。相反,str1直接作为null值输入,根据实现的不同可能会导致空引用异常。

不是 str1 导致了异常,因为我们通过更改以下内容解决了问题:this.s3 = Method(str2 + "._className", str1);变成了:this.s3 = str2 != null ? Method(str2 + "._className", str1) : str1; - Sylv21
1
@Sylv21,当str2null时,str1也可能为null,这意味着你没有看到str1null,因为对str2的检查停止了它。你有尝试过调试代码并检查哪个变量是null吗? - default
str1仅作为Method返回的默认值使用(请参见编辑后的问题)。 - Sylv21

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