11

在下面的代码中,我正在检查对象引用的相等性。

string x = "Some Text";
string y = "Some Other Text";
string z = "Some Text";

Console.WriteLine(object.ReferenceEquals(x, y)); // False
Console.WriteLine(object.ReferenceEquals(x, z)); // True
Console.WriteLine(object.ReferenceEquals(y, z)); // False

y = "Some Text";

Console.WriteLine(object.ReferenceEquals(x, y)); // True
Console.WriteLine(object.ReferenceEquals(x, z)); // True
Console.WriteLine(object.ReferenceEquals(y, z)); // True

这里:

  • x并且z指代同一个对象;我可以说那x是实习生并z使用了 taht 版本。好吧,我不确定这一点;请纠正我,如果我错了。
  • y我通过赋予它与 x 相同的值来更改它的值。我以为它会在这里创建一个新对象;但我错了,它使用了相同的参考。

我的问题是:

  • 是否对我使用的每个字符串都.net使用字符串实习生?
  • 如果是这样,是不是会影响性能?
  • 如果不是,在上面的例子中引用是如何变得相同的?
4

4 回答 4

14

是的,编译器中的常量字符串表达式用 处理ldstr,这保证了实习(通过MSDN):

公共语言基础结构 (CLI) 保证引用两个具有相同字符序列的元数据标记的两个 ldstr 指令的结果返回完全相同的字符串对象(称为“字符串实习”的过程)。

这不是每个字符串。它是代码中的常量字符串表达式。例如:

string s = "abc" + "def";

只有 1 个字符串表达式 - IL 将是“abcdef”上的 ldstr(编译器可以计算组合表达式)。

这不会影响性能。

运行时生成的字符串不会自动被实习,例如:

int i = GetValue();
string s = "abc" + i;

在这里,“abc”是实习的,但“abc8”不是。另请注意:

char[] chars = {'a','b','c'};
string s = new string(chars);
string t = "abc";

请注意st是不同的引用(文字(分配给t)是实习的,但新字符串(分配给s)不是)。

于 2012-08-17T10:25:41.230 回答
3

.net 是否对我使用的每个字符串都使用字符串实习生?

不,但它确实将它用于它在编译时知道的那些字符串,因为它们是代码中的常量。

string x = "abc"; //interned
string y = "ab" + "c"; //interned as the same string because the
                       //compiler can work out that it's the same as
                       //y = "abc" at compile time so there's no need
                       //to do that concatenation at run-time. There's
                       //also no need for "ab" or "c" to exist in your
                       //compiled application at all.
string z = new StreamReader(new FileStream(@"C:\myfile.text")).ReadToEnd();
                       //z isn't interned because it isn't known at compile
                       //time. Note that @"C:\myfile.text" is interned because
                       //while we don't have a variable we can access it by
                       //it is a string in the code.

如果是这样,是不是会影响性能?

不,它有助于提高性能:

首先:所有这些字符串都将在应用程序的内存中某处。实习意味着我们没有不必要的副本,因此我们使用的内存更少。第二:它使我们知道来自实习字符串的字符串比较速度非常快。第三:这并没有太多,但它给其他比较带来的提升确实如此。考虑存在于其中一个内置比较器中的这段代码:

public override int Compare(string x, string y)
{
    if (object.ReferenceEquals(x, y))
    {
        return 0;
    }
    if (x == null)
    {
        return -1;
    }
    if (y == null)
    {
        return 1;
    }
    return this._compareInfo.Compare(x, y, this._ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
}

这是为了排序,但同样适用于相等/不等式检查。要检查两个字符串是否相等或将它们按顺序排列,我们需要进行 O(n) 运算,其中 n 与字符串的长度成正比(即使在可以进行一些跳过和巧妙处理的情况下,它仍然是成正比的) . 这对于长字符串可能会很慢,并且比较字符串是很多应用程序在很多时候都会做的事情——这是一个提高速度的好地方。对于相等的情况,它也是最慢的(因为当我们发现差异时,我们可以返回一个值,但必须完全检查相等的字符串)。

即使您重新定义“等于”的含义(区分大小写、不区分大小写、不同的文化 - 一切仍然等于自身,并且如果您创建一个Equals()不遵循的覆盖,那么您将有一个错误)。一切总是在与它相等的东西相同的点上排序。这意味着两件事:

  1. 我们总是可以在不做任何工作的情况下考虑与自身相等的事物。
  2. 我们总是可以在0不做任何工作的情况下给出一个比较值来比较某物与它自己。

因此,上面的代码简化了这种情况,而无需进行更复杂和昂贵的比较。也没有不利的一面,因为如果我们不涵盖这种情况,我们将不得不添加一个测试,以测试两个值null无论如何都通过的情况。

现在,碰巧的是,某些算法的工作方式很自然地会自然而然地将某些事物与自身进行比较,所以它总是值得做的。但是,当我们具有不同值的两个字符串(例如xz在您的问题的开头)实际上相同时,字符串实习会增加时间,因此它会增加快捷方式对我们起作用的频率。

大多数时候这是一个很小的优化,但我们免费获得它,而且我们经常获得它,拥有它真是太好了。实际的收获——如果你正在写一个Equals或一个Compare考虑你是否也应该使用这个快捷方式。

那么一个相关的问题是“我应该实习一切吗?”

但是,在这里,我们必须考虑编译字符串没有的缺点。用字符串编译永远不会浪费实习,因为它们必须在某个地方。但是,如果您从文件中读取一个字符串,将其实习,然后再也不使用它,那么它将存在很长时间,这很浪费。如果你一直这样做,你可能会削弱你的记忆力。

假设您经常阅读包含一些标识符的一堆项目。您经常使用这些标识符将项目与来自其他来源的数据进行匹配。有一小组可以看到的标识符(比如只有几百个可能的值)。然后因为相等性检查是这些字符串的全部内容,而且它们并不多,所以实习(在读入的数据和您与之比较的数据上 - 否则毫无意义)成为一个胜利。

或者,假设有几千个这样的对象,我们与之匹配的数据总是缓存在内存中——这意味着无论如何这些字符串总是会在内存中的某个地方,所以实习成为一个不费吹灰之力的胜利。(除非有很多“未找到”结果的可能性 - 实习这些标识符只是为了找不到匹配项是失败的)。

最后,同样的基本技术可以用不同的方式完成。XmlReader例如,将它正在比较的字符串存储在一个NameTable类似于私有实习生池的 a 中,但整个事情可以在完成后收集。您还可以将该技术应用于在池化期间不会更改的任何引用类型(保证这一点的最佳方法是使其不可变,因此它在任何时候都不会更改)。将此技术用于具有大量重复的非常大的集合可以大大减少内存使用(我最大的节省至少是 16GB - 它可能更多,但在应用该技术之前服务器一直在该点附近崩溃)和/或速度向上比较。

于 2012-08-17T11:10:45.567 回答
1

字符串文字会自动被实习。

默认情况下,以编程方式创建的字符串不会被实习(用户输入的字符串也不会)。

在上面,“一些文本”和“一些其他文本”都已被实习,并且由于您在这些地方使用文字,您会看到实习版本是引用的版本。

在您的代码中,如果您有:

string.Format("{0} {1}", "Some", "Text")

您将看到返回的引用与其他文字不同。

于 2012-08-17T10:25:18.893 回答
1

我猜它又重复了

可能重复

奇怪的字符串文字比较

两个不同的“字符串”是同一个对象实例?

重复

The Common Language Infrastructure (CLI) guarantees that the result of two ldstr instructions referring to two metadata tokens that have the same sequence of characters return precisely the same string object (a process known as "string interning").
于 2012-08-17T10:45:26.763 回答