可能意外的参考比较按预期工作
Possible unintended reference comparison worked as intended
我有类似下面的代码:
this.Session[key] = "foo";
if ((this.Session[key] ?? string.Empty) == "foo")
{
//do stuff
}
这当然会造成 "Possible unintended reference comparison worked as intended" 情况。这个问题的解决方案在这里有详细的记录,我一看到代码就知道解决方法是将会话变量转换为 string
。
但是,代码已有多年历史,自最初编写以来从未更改过。直到本周,在我们的测试环境中,if
语句被评估为 true 并执行了 //do stuff
部分。此错误代码在我们的生产环境中仍按预期运行。
怎么会这样?编写的代码没有理由应该按预期工作;然而它确实如此,并且仍然在生产中。并且会发生什么变化,使本不应该运行但运行的代码突然停止运行(或者更确切地说,表现得像它应该一直运行的那样)?
你很幸运,比较成功了!在您的示例中,字符串 "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 关于此主题的相关且非常有趣的 post:
字符串文字“foo”是interned。这意味着每次使用它时,都会引用同一个对象。
The common language runtime conserves string storage by maintaining a table, called the intern pool, that contains a single reference to each unique literal string declared or created programmatically in your program. Consequently, an instance of a literal string with a particular value only exists once in the system.
For example, if you assign the same literal string to several variables, the runtime retrieves the same reference to the literal string from the intern pool and assigns it to each variable.
这就是为什么 object.ReferenceEquals("foo", "foo")
是正确的。
如果您对动态创建的字符串执行相同操作,它将不会被驻留并且引用将不相等
object str = new StringBuilder().Append("f").Append("oo").ToString(); // str = "foo"
object.ReferenceEquals(str, "foo"); // false
字符串驻留的工作方式因实现而异,这就是为什么在通过引用比较字符串时在不同的环境中会出现不同的行为。
我有类似下面的代码:
this.Session[key] = "foo";
if ((this.Session[key] ?? string.Empty) == "foo")
{
//do stuff
}
这当然会造成 "Possible unintended reference comparison worked as intended" 情况。这个问题的解决方案在这里有详细的记录,我一看到代码就知道解决方法是将会话变量转换为 string
。
但是,代码已有多年历史,自最初编写以来从未更改过。直到本周,在我们的测试环境中,if
语句被评估为 true 并执行了 //do stuff
部分。此错误代码在我们的生产环境中仍按预期运行。
怎么会这样?编写的代码没有理由应该按预期工作;然而它确实如此,并且仍然在生产中。并且会发生什么变化,使本不应该运行但运行的代码突然停止运行(或者更确切地说,表现得像它应该一直运行的那样)?
你很幸运,比较成功了!在您的示例中,字符串 "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 关于此主题的相关且非常有趣的 post:
字符串文字“foo”是interned。这意味着每次使用它时,都会引用同一个对象。
The common language runtime conserves string storage by maintaining a table, called the intern pool, that contains a single reference to each unique literal string declared or created programmatically in your program. Consequently, an instance of a literal string with a particular value only exists once in the system.
For example, if you assign the same literal string to several variables, the runtime retrieves the same reference to the literal string from the intern pool and assigns it to each variable.
这就是为什么 object.ReferenceEquals("foo", "foo")
是正确的。
如果您对动态创建的字符串执行相同操作,它将不会被驻留并且引用将不相等
object str = new StringBuilder().Append("f").Append("oo").ToString(); // str = "foo"
object.ReferenceEquals(str, "foo"); // false
字符串驻留的工作方式因实现而异,这就是为什么在通过引用比较字符串时在不同的环境中会出现不同的行为。