似乎.NET不再通过引用使值相等的字符串。
在LINQPad中,我尝试了以下内容,希望它绕过实习字符串常量:
var s1 = new string("".ToCharArray());
var s2 = new string("".ToCharArray());
object.ReferenceEquals(s1, s2).Dump();
但返回true
。但是,我想创建一个可以与任何其他string
对象可靠区分的string
。
(用例是创建一个用于可选参数的sentinel值。我正在包装WebForms'Page.Validate()
,我想根据调用者是否给我这个来选择适当的重载可选的验证组参数。所以我希望能够检测调用者是否省略了该参数,或者他是否传递了一个恰好等于我的默认值的值。显然,还有其他不那么神秘的方法来处理这个特定的用例,这个问题的目标更具有学术性。),
答案 0 :(得分:6)
似乎.NET竭力制造相同的字符串 按价值等于参考。
实际上,对于表现出类似行为的字符串,实际上只有两种特殊情况:
new
关键字(在类上)可能不会导致分配新对象。根据您的问题,我得到了您已经了解的第一个案例的印象。第二种情况是你偶然发现的那种情况。正如其他人所指出的那样,如果您只是继续使用非空字符串,您会发现创建一个非引用的字符串非常容易 - 等于程序中的任何其他字符串:
public static string Sentinel = new string(new char[] { 'x' });
作为一个小小的编辑,我实际上不会这么想(只要它有记录);但有点让我感到不安的是,CLR人员(?)实现了这个优化,而没有继续前进并对数组做同样的事情。也就是说,在我看来,他们也可能继续前进并使每个new T[0]
也引用同一个对象。或者,你知道,也没有为字符串做过。
答案 1 :(得分:3)
如果字符串是ReferenceEqual,则它们是同一个对象。当你调用new string(new char[0])
时,你没有得到恰好是引用的新对象 - 等于string.Empty;那是不可能的。相反,您将获得对已创建的string.Empty实例的新引用。这是字符串构造函数中特殊情况代码的结果。
试试这个:
var s1 = new string(new char { 'A', 'b' });
var s2 = new string(new char { 'A', 'b' });
object.ReferenceEquals(s1, s2).Dump();
另外,请注意字符串常量是实例化的,因此代码中文字"Ab"
的所有实例都将彼此相等,因为它们都引用相同的字符串对象。常量折叠也适用,因此常量表达式"A" + "b"
也将等于"Ab"
。
因此,您的sentinal值可以是私有创建的非零长度字符串。
答案 2 :(得分:1)
您可以将不可打印的字符放入字符串...甚至是0 / nul字符。但实际上,我只是使用null
作为sentinel值,并尝试确保其他地方的代码使用空字符串而不是null。
答案 3 :(得分:0)
所以我希望能够检测调用者是否省略了该参数,或者他是否传递了一个恰好等于我的默认值的值。
我之前从未这样做过,但我的想法是制作一个Nullable
课程...但不是Nullable
而是Parameter
并且会跟踪是否null
或者没有分配任何东西(包括{{1}})。