Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 空字符串作为特例?_C#_.net - Fatal编程技术网

C# 空字符串作为特例?

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

我读了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.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