我读了乔恩·斯基特的测验,我想知道为什么我的第二个样本不起作用,而第一个样本起作用。
为什么会产生true:
1 2 3
| object x = new string("".ToArray());
object y = new string("".ToArray());
Console .WriteLine(x == y ); //true |
但这一条并没有:
1 2 3 4 5
| 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 |
我用的是fw4.5和vs2010。
幸运的是,我也安装了VS2005,结果相同:
- 您使用的.NET版本允许您将单个字符串元素带到数组中?或者我应该问你在使用什么?
- @adamzuckerman字符串是一个IEnumerable字符…
- 所以你也包括Linq?
- @阿达姆扎克曼是的。
- @Adamzuckerman没关系。如果你想用String.ToCharArray的话,也一样。
- 如果你像这样比较,那么它将返回真值。
- 不需要intern,因为默认情况下,文本是内部的。另外,您是否可以只使用.net3.5或.net4.0检查机器?因为String.Empty的行为从.NET 4.5改为.NET 4.5,这可能就是原因!
- @但这是价值比较,伙计,我们在这里讨论的是参考平等。
- 如果您将第一个示例更改为使用object x = new String("k".ToArray());,则比较也会失败。
- 好吧,我不知道为什么会这样?
- 这真是个好问题
- @dholakiyaankit,因为字符串是按值而不是参照进行比较的,请参阅:msdn.microsoft.com/en-us/library/362314fe.aspx
- 非常感谢你@selman22
- 那么,如果按值比较,"k"字符串不应该等于其他"k"字符串吗?
- 此外,使用object c = new String(String.Empty.ToArray());与true相比。
- @Adamzuckerman,但它们被定义为对象,因此通过引用进行比较。如果您将它们定义为字符串,那么它将返回true。
- @罗伊纳米尔,就像我之前说的,有人必须用一台甚至没有安装.NET 4.5的机器来测试这个,你安装了.NET 4.5,不是吗?所以这不会有帮助,因为新的.net4.5二进制文件将在那里使用,即使您的目标是.net 2.0。
- @sriramsakthivel 4.5扩展/覆盖fw 4。不是2。如果安装了2,它将使用它。(imo)(我确实安装了它)
- @罗伊纳米尔是的,这可能是错的。
- 使用新的引用源,我们可以很容易地在源代码中看到:string.cs,第1687行和第1722行。当然,这只是一个实现,您应该依赖于规范,而不是源代码。
- @Kobi请将您的评论转换为答案。
- @Royi-我在阅读了最上面的答案后找到了它,所以它已经存在了:"一些版本的.NET运行时会在运行时自动插入空字符串,一些则不会!"但是谢谢!
- @Kobi我在选择它时遇到了问题,因为98%的答案是我已经知道和提到的。(就像我说的:没用)。只有他最后一句话才真正回答了这个问题。但是您的也提供了源代码。
- 有人能给测验添加一个链接吗?
- 我不知道你为什么接受下面的答案,它甚至没有解决问题的内容。在第一个示例中,使用字符串构造函数实例化空字符串。这本应给出全新的参考,但并非如此。
- @杰夫梅卡多,你没有读答案。运行时中的空字符串也很有趣
- 是的,但文字上的"以东十一号"(5)和"以东十一号"(6)不同于"以东十一号"(7)。据我所见,没有一个答案能真正解决这个问题。埃里克·利珀特的博客引用的话断章取义了。它指的是空字符串和String.Empty。两个独立换行的字符串恰好为空的情况仍然不清楚。所有的答案都很方便地围绕着这个解释跳来跳去。
- @Jeff有些版本的.NET运行时会在运行时自动插入空字符串,有些则不会。因此,新字符串(".ToArray())的值也将感兴趣,因为它是一个空字符串
- 是的,我可能太沉迷于这一点了。不管什么原因,在博客的上下文中读到的内容对我来说都是不同的。对于所有的人来说,不总是真实的想法现在正让我心烦意乱。
下面是EricLippert的一篇博客文章,它回答了你的问题:string interning和string.empty。
他描述了类似的情况:
1 2 3 4 5 6
| 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 !? |
所以这个想法是,实习并不意味着你只有一个特定的string实例,即使是实习的时候。默认情况下,只有编译时的文本是内部的。这意味着以下代码打印正确:
1 2 3
| var k1 ="k";
object k2 ="k";
Console.WriteLine(k1 == k2); |
但是,如果您试图在运行时以编程方式创建带有"k"内容的字符串,例如使用string(char[])构造函数、在对象上调用ToString()、使用StringBuilder等,则默认情况下不会得到interned字符串。这张印的是假的;
1 2 3
| var k1 ="k";
object k2 = new string("k".ToCharArray());
Console .WriteLine(k1 == k2 ); |
为什么?因为在运行时插入字符串是昂贵的。
There Ain't No Such Thing As A Free Lunch.
(...)
In short, it is in the general case not worth it to intern all strings.
关于空字符串的不同行为:
Some versions of the .NET runtime automatically intern the empty string at runtime, some do not!
- 那么为什么console.writeline(str1和str2 = TRUE);打印
- 因为他们是被作为"他string和string==算子类重写执行字符串比较,需要参考平等的检查。
- 这是要做什么的问题好吗?在给定的字符串的所有实例,通过constructors是创建。字符串是用literals(NO和NO(interning)。
- 请解释它如何ReferenceEquals(new string(new char[]{}), new string(new char[]{})) == true是可能的。我一直就这样在事后来劝告本已做标记extern构造函数是在参考源,即它不是一个东西可以被实现,纯c #,好吗?
注意,在第二个代码块中插入新字符串确实使它们相等。
1 2 3 4
| var k ="k";
object x = string.Intern(new string(k .ToArray()));
object y = string.Intern(new string(k .ToArray()));
Console .WriteLine(x == y ); //true |
它看起来像是自动插入空字符串,但是非空字符串不会被插入,除非它们是显式执行的(或者它们是一直被插入的文本字符串)。
我猜是的,空字符串被视为一种特殊情况,并被自动截取,可能是因为检查非常简单,不会增加任何实际的性能惩罚(我们可以安全地说,任何长度为0的字符串都是空字符串,并且与任何其他空字符串相同——所有其他字符串都要求我们查看字符而不仅仅是长度)。
- 这些常数是interned literals默认字符串。
- 本代码是相同的,因为返回true。默认为object x ="k"; object y ="k"实习生。问题是为什么在新string(k.ToArray())给新实例在AS new string("".ToArray())给interned实例?
第一种情况比较两个对同一对象的引用(String.Empty)。对2个object变量调用operator==会引起它们的引用比较,并给出true的结果。
第二种情况产生两个不同的字符串类实例。他们的参考比较给出了false。
如果在第二种情况下,将x和y类型赋给string类型,将调用string.operator==覆盖,比较则得出true类型。
注意,在这两种情况下,我们不直接处理字符串插入。我们比较的字符串对象是使用string(char[])构造函数创建的。显然,构造函数设计为在使用空数组作为参数调用时返回String.Empty字段的值。
Marcinaraszek发布的答案参考了Lippert的博客,该博客讨论了字符串实习。那个博客文章讨论了字符串类的其他用法。从前面提到的Lippert的博客中可以看到这个例子:
1 2 3 4 5 6
| 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?! |
我们在这里看到的是,空字符串文本(""的赋值不能保证生成对静态只读System.String.Empty字段的引用。
让我们来看看object x = new string("".ToArray());表达式的IL:
1 2 3 4
| 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_线。无论文本是否被截取,ToArray()方法都会生成一个新的空数组,而String::.ctor(char[])方法会给出String.Empty。
我们在这里看到的不是String.Empty的特殊情况,而是string类作为引用类型同时不可变的副作用之一。还有其他不可变的框架类型,它们的预定义值具有相似的语义(如DateTime.MinValue)。但据我所知,这种框架类型被定义为struct,而不像string,后者是一种引用类型。价值类型完全不同…从可变类构造函数返回某些固定的预定义类型实例是没有意义的(调用代码将能够更改该实例并导致类型的不可预测行为)。因此,如果这些类型是不可变的,则其构造函数并不总是返回新实例的引用类型可能存在。不过,我不知道框架中的其他此类类型,除了string。
- 在第二个案例2个不同的子类的实例的字符串确定。如果那么为什么不先一子帧的2个不同的字符串类的实例?
- "royinamir字符串(字符[可能] ctor)是这样设计的。NET对象有特殊空字符串。因此,当它接收的string.Empty构造函数返回空数组。
- "pavelzhuravlev:有组织的任何其他类型的新实例constructors不总是返回?
- "supercat:我已经更新我的地址您的问题的答案
- "royinamir:我想澄清我的观点在这里。请看到更新的答案。
我的假设是为什么第一个得出正确而第二个得出错误:
我的第一个结果是一个优化,取下面的代码
1
| Enumerable.Empty<char>() == Enumerable.Empty<char>() // true |
因此,假设当字符串为空时,ToArray方法返回Enumerable.Empty(),这就解释了为什么第一个结果为真而第二个结果不为真,因为它正在进行引用检查。
有一种特殊情况,空字符串总是返回相同的对象,这就是为什么在比较对象是否相同时,在这种情况下它是真的。
[编辑]:以前的代码使用字符串比较器而不是对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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); |
结果
我想这就是我引用乔恩·斯基特答案的原因关于字符串比较
string.equals()和==运算符是否真的相同?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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(); |
产量
1 2 3 4 5 6
| False
Address x1:0x2613E5
Address y1:0x2613E5
False
Address x:0x2613E5
Address y:0x2613E5 |
根据http://msdn.microsoft.com/en-us/library/system.string.intern(v=vs.110).aspx
In the .NET Framework 3.5 Service Pack 1, the Intern method reverts to its behavior in the .NET Framework 1.0 and 1.1 with regard to interning the empty string...
...In the .NET Framework 1.0, .NET Framework 1.1, and .NET Framework 3.5 SP1, ~empty strings~ are equal
这意味着,默认情况下,空字符串都是内部的,即使是从空数组构造的,因此是相等的。
此外:
The .NET Framework version 2.0 introduces the CompilationRelaxations.NoStringInterning enumeration member
这很可能为您提供了一种创建一致的比较方法,尽管正如@benn建议的那样,您更愿意显式地使用intern函数。
考虑到拳击,你也可以用string.Equals代替==。