可能的意外参考比较按预期工作

时间:2015-12-15 20:48:29

标签: c# asp.net .net-4.5

我的代码类似于以下内容:

this.Session[key] = "foo";

if ((this.Session[key] ?? string.Empty) == "foo")
{
    //do stuff
}

当然,这会产生一个"可能的非预期参考比较按预期工作"情况。这里有很好的解决方案,我已经知道修复是在看到代码后立即将会话变量强制转换为string

但是,代码已有多年历史,并且自最初编写以来从未更改过。直到本周在我们的测试环境中,if语句被评估为true并执行//do stuff部分。这个错误的代码仍然在我们的生产环境中按预期工作。

这怎么可能?这样编写的代码没有理由按预期工作;然而它确实如此,并且仍然在生产中。什么会改变,使这个代码不应该工作,但突然停止工作(或者更确切地说,表现得像它应该有的那样)?

2 个答案:

答案 0 :(得分:4)

字符串文字“foo”是interned。这意味着每次使用它时,都会引用相同的对象。

  

公共语言运行库通过维护一个名为intern pool的表来保存字符串存储,该表包含对程序中以编程方式声明或创建的每个唯一文字字符串的单个引用。因此,具有特定值的文字字符串实例仅在系统中存在一次。

     

例如,如果将相同的文字字符串分配给多个变量,则运行时将从实习池中检索对文字字符串的相同引用,并将其分配给每个变量。

这就是object.ReferenceEquals("foo", "foo")为真的原因。

如果对动态创建的字符串执行相同操作,则不会实现该字符串,并且引用将不相等

object str = new StringBuilder().Append("f").Append("oo").ToString(); // str = "foo"
object.ReferenceEquals(str, "foo");                                   // false

字符串实习可以根据实现的不同而有所不同,这就是为什么在通过引用比较字符串时可以在不同环境中获得不同的行为。

答案 1 :(得分:2)

你很幸运,比较有效!在您的示例中,字符串“foo”为interned,即两个字符串文字都存储一次并具有相同的引用。但是,如果两个foo中的一个在另一个程序集中定义或被反序列化或以某种方式在代码中构造(例如string s = "f"; string t = s + "oo";),则引用可能不同。由于Session[key]被输入为对象,因此将执行参考比较。

合并是没有必要的,但铸造是:

if ((string)Session[key] == "foo") { ... } // This will perform a textual comparison.

你也可以这样写(因为Equals是多态的):

if (Session[key].Equals("foo")) { ... } // This will perform a textual comparison.

仅比较2个字符串值是不够的,它们必须静态输入string才能作为字符串进行比较。

相关的,关于此主题的Jon Skeet非常有趣的帖子:https://stackoverflow.com/a/3678810/880990