C# 空字符串作为特例?
我读了Jon Skeet的测验,我想知道为什么我的第二个样本不起作用,而第一个却起作用 为什么这会产生C# 空字符串作为特例?,c#,.net,C#,.net,我读了Jon Skeet的测验,我想知道为什么我的第二个样本不起作用,而第一个却起作用 为什么这会产生正确的: 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.ToArra
正确的:
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
我在vs2010中使用FW4.5
幸运的是,我还安装了vs2005,结果相同:
我的假设是为什么第一个结果为真而第二个结果为假:
我的第一个结果是优化,采用以下代码
Enumerable.Empty<char>() == Enumerable.Empty<char>() // true
Enumerable.Empty()==Enumerable.Empty()//真
因此,假设ToArray
方法在字符串为空时返回Enumerable.Empty()
,这将解释为什么第一个结果为true,第二个结果为true,因为它正在执行引用检查。请注意,在第二个代码块中插入新字符串会使它们相等
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的字符串都是空字符串,并且与任何其他空字符串相同——所有其他字符串都要求我们查看字符,而不仅仅是长度)。第一种情况比较对同一对象的两个引用(string.empty
)对2个对象
变量调用运算符==
,通过引用导致它们的比较,并给出真值
第二种情况产生两个不同的string类实例。它们的引用比较给出false
如果在第二种情况下将string
类型赋予x
和y
,将调用string.operator=
覆盖,比较结果为true
请注意,在这两种情况下,我们都不直接处理字符串内部。我们比较的字符串对象是使用string(char[])
constructor创建的。显然,该构造函数设计用于在使用空数组作为参数调用时返回string.Empty
字段的值
由MarcinJuraszek referes发布的关于字符串交互的答案。该博客文章讨论了字符串类用法的其他拐角情况。从前面提到的利珀特的博客中考虑这个例子:
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
字段的引用
让我们看看对象x=new字符串(“.ToArray());
表达式的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:ldstr“”
IL_0006:call!!0[][System.Core]System.Linq.Enumerable::ToArray(class[mscorlib]System.Collections.Generic.IEnumerable`1)
IL_000b:newobj实例无效[mscorlib]系统。字符串::.ctor(字符[])
IL_0010:stloc.0
插入可能(也可能不)发生在IL_0001行。无论文本是否被插入,ToArray()
方法都会生成一个新的空数组,而String::.ctor(char[])
会为我们提供String.empty
这里我们看到的不是string.Empty
的特例,而是string
类同时作为引用类型和不可变的副作用之一。还有其他不可变的框架类型,它们具有类似语义的预定义值(如DateTime.MinValue
)但据我所知,这种框架类型被定义为struct
,而不像string
那样是一种引用类型。值类型完全是不同的故事…从可变类构造函数返回一些固定的预定义类型实例是没有意义的(调用代码将能够更改该实例并导致类型的不可预测行为)。因此,如果引用类型的构造函数不总是返回新实例,则这些引用类型可能存在,前提是这些类型是不可变的。不过,我不知道框架中还有其他此类类型,除了根据
在.NET Framework 3.5 Service Pack 1中,Intern方法恢复到其在.NET Framework 1.0和1.1中关于插入空字符串的行为…
…在.NET Framework 1.0、.NET Framework 1.1和.NET Framework 3.5 SP1中,~empty strings~相等
这意味着,默认情况下,即使从空数组构造空字符串,空字符串也都是内部的,因此它们是相等的
此外:
2.0版.NET Framework引入了CompliationRelaxations.NoStringInterning枚举成员
这很可能为您提供了一种创建一致的比较方法的方法,尽管正如@BenM所建议的,您更愿意显式地使用Intern函数
考虑到出现的装箱问题,您也可以使用string.Equals
而不是=
以下是Eric Lippert的一篇博客文章,它回答了您的问题:
他描述了类似的情况:
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 !?
因此,我们的想法是,实习并不意味着您将只有一个特定字符串的实例,即使在实习时也是如此。默认情况下,只实习编译时文本。。这意味着以下代码打印为真:
var k1 = "k";
object k2 = "k";
Console.WriteLine(k1 == k2);
但是,如果您试图在运行时以编程方式使用“k”
内容创建字符串,例如使用string(char[])
构造函数,在对象上调用ToString()
,使用StringBuilder
,等等,默认情况下,您将不会得到内部字符串。此字符串打印为false
var k1 = "k";
object k2 = new string("k".ToCharArray());
Console.WriteLine(k1 == k2);
为什么?因为在运行时实习字符串很昂贵
天下没有免费的午餐
(……)
简而言之,它
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
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