6

.NET 似乎不遗余力地使值相等的字符串通过引用相等。

在 LINQPad 中,我尝试了以下方法,希望它能绕过内部字符串常量:

var s1 = new string("".ToCharArray());
var s2 = new string("".ToCharArray());

object.ReferenceEquals(s1, s2).Dump();

但这会返回true。但是,我想创建一个string可以可靠地区分于任何其他对象的string对象。

(用例是创建一个用于可选参数的哨兵值。我正在包装 WebForms' Page.Validate(),我想根据调用者是否给我可选的验证组参数来选择适当的重载。所以我希望能够检测调用者是否省略了该参数,或者他是否传递了一个恰好等于我的默认值的值。显然还有其他不那么神秘的方法来处理这个特定的用例,这个问题的目的更具学术性。),

4

4 回答 4

6

.NET 似乎不遗余力地使值相等的字符串通过引用相等。

实际上,实际上只有两种特殊情况下的字符串表现出您在此处描述的行为:

  1. 代码中的字符串文字是实习的,因此两个地方的相同文字将导致对同一对象的引用。
  2. 字符串是一个特别奇怪的情况,据我所知,.NET 程序中的每个空字符串实际上都是同一个对象(即,“每个空字符串”构成一个字符串)。这是我在 .NET 中知道的唯一一种情况,其中使用new关键字(在类上)可能不会导致分配新对象。

从您的问题中,我得到您已经知道第一个案例的印象。第二种情况是您偶然发现的情况。正如其他人指出的那样,如果您继续使用非空字符串,您会发现创建一个与程序中的任何其他字符串引用不相等的字符串非常容易:

public static string Sentinel = new string(new char[] { 'x' });

抛开一点社论不谈,我实际上并不太介意这一点(只要它被记录在案);但是让我很恼火的是,CLR 的人(?)实现了这种优化,却没有继续对数组做同样的事情。也就是说,在我看来,他们还不如继续并让每个都new T[0]指向同一个对象。或者,你知道,也没有为字符串这样做。

于 2012-12-12T03:43:07.453 回答
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"

因此,您的标记值可以是私人创建的非零长度字符串。

于 2012-12-12T03:21:20.647 回答
1

您可以将不可打印的字符放入字符串中......甚至是 0/nul 字符。但实际上,我只是使用null哨兵值,并尝试确保其他地方的代码使用空字符串而不是 null。

于 2012-12-12T03:29:04.950 回答
0

所以我希望能够检测调用者是否省略了该参数,或者他是否传递了一个恰好等于我的默认值的值。

我以前从未这样做过,但我的想法是开设一个Nullable课程......但不是Nullable它会Parameter并且会跟踪它是否被分配了任何东西(包括null)。

于 2012-12-12T03:10:43.007 回答