c#-空字符串是一种特殊情况?

我读了乔恩·斯基特(Jon Skeet)的测验,不知道为什么我的第二个样本不能像第一个样本那样起作用。

为什么会产生true

object x = new string("".ToArray());
object y = new string("".ToArray());
Console.WriteLine(x == y); //true

但这不是:

var k="k";
//string.intern(k); // doesn't help
object x = new string(k.ToArray());
object y = new string(k.ToArray());
Console.WriteLine(x == y); //false

我正在将vs 4.5与fw 4.5一起使用。

幸运的是,我还安装了vs2005,结果相同:

enter image description here

Royi Namir asked 2019-11-17T01:52:51Z
7个解决方案
43 votes

这是Eric Lippert的博客文章,回答了您的问题:String interning和String.Empty。

他正在描述类似的情况:

object obj = "Int32";
string str1 = "Int32";
string str2 = typeof(int).Name;
Console.WriteLine(obj == str1); // true
Console.WriteLine(str1 == str2); // true
Console.WriteLine(obj == str2); // false !?

因此,想法是,实习并不意味着您只有一个特定的实例"k",即使在实习期间也是如此。 默认情况下,仅编译时文字是intern。 这意味着以下代码显示为true:

var k1 = "k";
object k2 = "k";
Console.WriteLine(k1 == k2);

但是,如果您尝试在运行时以编程方式使用"k"内容创建字符串,例如 使用string(char[])构造函数,在对象上调用ToString(),使用StringBuilder,依此类推,默认情况下您将不会获得实习生字符串。 这个打印错误;

var k1 = "k";
object k2 = new string("k".ToCharArray());
Console.WriteLine(k1 == k2);

为什么? 因为在运行时实习字符串很昂贵。

没有免费的午餐。

(...)

简而言之,通常情况下,不值得实习所有字符串。

关于空字符串的不同行为:

.NET运行时的某些版本会在运行时自动实习空字符串,有些则不会!

MarcinJuraszek answered 2019-11-17T01:54:10Z
11 votes

请注意,在第二个代码块中插入新字符串确实会使它们相等。

var k="k";
object x = string.Intern(new string(k.ToArray()));
object y = string.Intern(new string(k.ToArray()));
Console.WriteLine(x == y); //true

看起来好像是自动对空字符串进行了intern,但是除非明确地完成了非空字符串,否则它们不会被intern(或者它们是始终被intern的文字字符串)。

我猜测是的,空字符串被视为一种特殊情况并自动被自动检查,这可能是因为检查是如此琐碎,以至于它不会增加任何实际的性能损失(我们可以肯定地说长度为0的任何字符串都是 空字符串,并且与任何其他空字符串相同-所有其他字符串都要求我们查看字符,而不仅仅是长度)。

BenM answered 2019-11-17T01:54:50Z
6 votes

第一种情况将2个引用与同一对象进行比较(string.Empty)。 调用string以获得2个DateTime.MinValue变量,以使其通过引用进行比较,并给出struct

第二种情况产生两个不同的字符串类实例。 他们的参考比较得出string.Empty

如果在第二种情况下将string.Empty类型赋予stringDateTime.MinValue,则将调用struct替代,并且比较结果为2617547773230730711812

请注意,在两种情况下,我们都不直接处理字符串interning。 我们比较的字符串对象是使用string.Empty构造函数创建的。 显然,该构造函数旨在以空数组作为参数调用时返回2617547773230730711809字段的值。

MarcinJuraszek发布的答案引用了Lippert的博客,该博客讨论了字符串实习。 该博客文章讨论了字符串类用法的其他特殊情况。 请考虑上述Lippert博客中的示例:

object obj = "";
string str1 = "";
string str2 = String.Empty;
Console.WriteLine(obj == str1); // true
Console.WriteLine(str1 == str2); // true
Console.WriteLine(obj == str2); // sometimes true, sometimes false?!

我们在这里看到的是,不能保证空字符串文字(string.Empty)的赋值会产生对静态只读string字段的引用。

让我们看一下string.Empty表达式的IL:

IL_0001:  ldstr      ""
IL_0006:  call       !!0[] [System.Core]System.Linq.Enumerable::ToArray<char>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
IL_000b:  newobj     instance void [mscorlib]System.String::.ctor(char[])
IL_0010:  stloc.0

实习可能会(也可能不会)发生在IL_0001行。 不管文字是否经过内插,string.Empty方法都会产生一个新的空数组,而261754777323071111809会给我们DateTime.MinValue

我们在这里看到的不是261754777323071111808的特殊情况,而是261754777323071111809类的副作用之一,该副作用是引用类型并且同时不可变。 还有其他不可变框架类型,它们具有具有类似语义的预定义值(例如DateTime.MinValue)。 但是据我所知,与引用类型string不同,此类框架类型被定义为struct。 值类型是完全不同的故事...从可变的类构造函数中返回一些固定的预定义类型实例是没有意义的(调用代码将能够更改该实例并导致类型的不可预测的行为)。 因此,只要那些构造是不可变的,它们的构造函数并不总是返回新实例的引用类型可能存在。 除了string外,我在框架中还没有发现其他此类类型。

Pavel Zhuravlev answered 2019-11-17T01:56:14Z
4 votes

我的假设是,为什么第一个产生true,而第二个产生false:

我的第一个结果是优化,采用以下代码

Enumerable.Empty<char>() == Enumerable.Empty<char>() // true

因此,假设当字符串为空时ToArray方法返回Enumerable.Empty<char>(),这将解释为什么第一个结果产生true而第二个结果却不正确,因为它正在执行引用检查。

Matthew answered 2019-11-17T01:56:59Z
2 votes

根据[http://msdn.microsoft.com/zh-cn/library/system.string.intern(v=vs.110).aspx]

string.Equals

string.Equals

这意味着,即使从空数组构造时,默认情况下,空字符串都将被保留,因此它们是相等的。

此外:

string.Equals

这很可能为您提供了一种创建一致的比较方式的方法,尽管正如@BenM建议的那样,您宁愿显式使用Intern函数。

考虑到发生的拳击,您还可以使用string.Equals而不是==

Todd answered 2019-11-17T01:58:01Z
2 votes

我认为这可能是我推荐Jon Skeet的原因关于字符串比较

string.Equals()和==运算符真的一样吗?

        object x1 = new StringBuilder("").ToString().ToArray();
        object y1 = new StringBuilder("").ToString().ToArray();
        Console.WriteLine(x1 == y1); //true

        Console.WriteLine("Address x1:" + Get(x1));
        Console.WriteLine("Address y1:" + Get(y1));

        var k = "k";
        //string.intern(k); // doesn't help
        object x = new string(k.ToArray());
        object y = new string(k.ToArray());
        Console.WriteLine(x == y); //false

        Console.WriteLine("Address x:" + Get(x));
        Console.WriteLine("Address y:" + Get(y));

        Console.Read(); 

输出量

False
Address x1:0x2613E5
Address y1:0x2613E5
False
Address x:0x2613E5
Address y:0x2613E5
Ganesh_Devlekar answered 2019-11-17T01:58:41Z
2 votes

在特殊情况下,空字符串总是返回相同的对象,这就是为什么在这种情况下比较对象是否相同时的原因。

[编辑]:先前的代码使用字符串比较器代替对象

object a = "s";
object b = "d";

a = ((string)a).Replace("s", "");
b = ((string)b).Replace("d", "");

Console.WriteLine(a == b);

object c = "sa";
object d = "da";

c = ((string)c).Replace("s", "");
d = ((string)d).Replace("d", "");

Console.WriteLine(c == d);

c = ((string)c).Replace("a", "");
d = ((string)d).Replace("a", "");

Console.WriteLine(c == d);

结果

True
False
True
Pedro.The.Kid answered 2019-11-17T01:59:19Z
translate from https://stackoverflow.com:/questions/22138144/empty-string-as-a-special-case