Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/325.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/hadoop/6.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#_String_Reference_Clr_String Interning - Fatal编程技术网

C# 字符串实习?

C# 字符串实习?,c#,string,reference,clr,string-interning,C#,String,Reference,Clr,String Interning,第二个ReferenceEquals调用返回false。为什么s4中的字符串没有被实习?(我不在乎StringBuilder相对于字符串连接的优势。) 当我做String.Intern时(s4),我仍然得到false 这里,s3和s4都是实习生,但他们的参考值不相等 string s3 = "tom"; string s4 = "to"; s4 += "m"; String.Intern(s4); Console.WriteLine(s3 == s4); //true Console.Writ

第二个ReferenceEquals调用返回false。为什么s4中的字符串没有被实习?(我不在乎StringBuilder相对于字符串连接的优势。)

当我做
String.Intern时(s4),我仍然得到false

这里,s3和s4都是实习生,但他们的参考值不相等

string s3 = "tom";
string s4 = "to";
s4 += "m";
String.Intern(s4);

Console.WriteLine(s3 == s4); //true
Console.WriteLine(object.ReferenceEquals(s3, s4)); //false
Console.WriteLine(string.IsInterned(s3) != null);  //true (s3 is interned)
Console.WriteLine(string.IsInterned(s4) != null);  //true (s4 is interned)

字符串是不可变的。这意味着它们的内容无法更改

当你做
s4+=“m”
在内部,CLR将字符串复制到内存中包含原始字符串和附加部分的另一个位置


请参阅。

已插入
s4
中的字符串。但是,当您执行
s4+=“m”,您创建了一个新字符串,该字符串将不会被插入,因为它的值不是字符串文字,而是字符串连接操作的结果。因此,
s3
s4
是位于两个不同内存位置的两个不同字符串实例

有关字符串实习的更多信息,请参见最后一个示例。当您执行
String.Intern(s4)
时,您确实是在对字符串进行实习,但仍然没有在这两个实习字符串之间执行引用相等性测试。
String.Intern
方法返回插入的字符串,因此需要执行以下操作:

string s1 = "tom";
string s2 = "tom";

Console.Write(object.ReferenceEquals(s2, s1)); //true 

string s3 = "tom";
string s4 = "to";
s4 += "m";

Console.Write(object.ReferenceEquals(s3, s4)); //false

string s5 = String.Intern(s4);

Console.Write(object.ReferenceEquals(s3, s5)); //true
在C#中,每个字符串都是一个不同的对象,无法编辑。您正在创建对它们的引用,但每个字符串都是不同的。行为一致且易于理解


我是否可以建议检查
StringBuilder
类,以便在不创建新实例的情况下操作字符串?它应该足以满足您对字符串的任何操作。

首先,到目前为止,关于不可变字符串的所有内容都是正确的。但是有些重要的东西是没有写出来的。代码

string s1 = "tom";
string s2 = "tom";
Console.Write(object.ReferenceEquals(s2, s1)); //true
显示“True”,但这仅仅是因为一些小的编译器优化或类似的原因,因为CLR忽略C编译器属性(请参阅“CLR通过C”一书),并且只在堆中放置一个字符串“tom”

其次,您可以使用以下行修复这种情况:

s3 = String.Intern(s3);
s4 = String.Intern(s4);
Console.Write (object.ReferenceEquals (s3, s4)); //true
函数
String.Intern
计算字符串的哈希代码,并在内部哈希表中搜索相同的哈希。因为它找到了这个,所以它返回对已经存在的
String
对象的引用。如果该字符串在内部哈希表中不存在,则生成该字符串的副本并计算哈希。垃圾收集器不会为字符串释放内存,因为它被哈希表引用。

来源:

字符串插入是编译器使用的一种优化技术。如果在一个编译单元中有两个相同的字符串文字,那么生成的代码将确保在程序集中为该文字的所有实例(用双引号括起来的字符)创建一个字符串对象

我来自C#背景,所以我可以举一个例子来解释:

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 !?
注1:对象通过引用进行比较

注2:typeof(int)。名称由反射方法计算,因此不会在编译时计算这些比较是在编译时进行的。

结果分析: 1) 因为它们都包含相同的文本,所以生成的代码将只有一个引用“Int32”的对象见注1

2) true,因为两个值的内容都被检查为相同


3) 错误,因为str2和obj不具有相同的文本。请参见注释2

比较两个对象(而不是字符串)时,不会调用字符串相等运算符,因为它是没有多态性的静态方法

下面是一个测试:

static void Test()
{
  object o1 = "a";
  object o2 = new string("a".ToCharArray());

  string o3 = "a";
  string o4 = new string("a".ToCharArray());

  object o5 = "a"; // Compiler optimization addr(o5) = addr(o6)
  object o6 = "a";

  string o7 = "a"; // Compiler optimization addr(o7) = addr(o8)
  string o8 = "a";

  Console.WriteLine("Enter same text 4 times:");

  object o9 = Console.ReadLine();
  object o10 = Console.ReadLine();

  string o11 = Console.ReadLine();
  string o12 = Console.ReadLine();

  Console.WriteLine("object arr   o1  == o2  ? " + ( o1 == o2 ).ToString());
  Console.WriteLine("string arr   o3  == o4  ? " + ( o3 == o4 ).ToString());
  Console.WriteLine("object const o5  == o6  ? " + ( o5 == o6 ).ToString());
  Console.WriteLine("string const o7  == o8  ? " + ( o7 == o8 ).ToString());
  Console.WriteLine("object cnsl  o9  == o10 ? " + ( o9 == o10 ).ToString());
  Console.WriteLine("string cnsl  o11 == o12 ? " + ( o11 == o12 ).ToString());
  Console.WriteLine("o1.Equals(o2) ? " + o1.Equals(o2).ToString());
  Console.WriteLine("o3.Equals(o4) ? " + o3.Equals(o4).ToString());
  Console.WriteLine("o5.Equals(o6) ? " + o5.Equals(o6).ToString());
  Console.WriteLine("o7.Equals(o8) ? " + o7.Equals(o8).ToString());
  Console.WriteLine("o9.Equals(o10) ? " + o9.Equals(o11).ToString());
  Console.WriteLine("o11.Equals(o12) ? " + o11.Equals(o12).ToString());
}
结果:

object arr   o1  == o2  ? False
string arr   o3  == o4  ? True
object const o5  == o6  ? True
string const o7  == o8  ? True
object cnsl  o9  == o10 ? False
string cnsl  o11 == o12 ? True
o1.Equals(o2) ? True
o3.Equals(o4) ? True
o5.Equals(o6) ? True
o7.Equals(o8) ? True
o9.Equals(o10) ? True
o11.Equals(o12) ? True

我知道字符串是不可变的。但是实习的全部目的是为了节省内存,对吗?为什么CLR不能说,嘿,我的实习生池中有相同的值,我只是要指向它。@rkrauter:在每次操作之后,检查实习生池中的所有字符串是否有任何字符串等于操作的结果是相当昂贵的!因此,CLR牺牲了内存效率来提高执行速度。编译时的字符串计算可能很慢,因此其结果可以保存。运行时的计算必须是快速的,所以对照一系列其他字符串检查每个结果似乎是不切实际的。那么字符串的插入主要是在编译时完成的?刚注意到当我做弦乐实习生(s4);,我还是会弄错。请解释。如果需要将大字符串连接在一起,请仅使用StringBuilder。在所有其他情况下,节省的时间非常少,这无关紧要。此外,字符串被放入内部已创建字符串的数组中,这意味着,如果您创建字符串“Hello”,任何进一步的字符串“Hello”都将指向内存中的同一引用。请使用s4=string.Intern(s4)再验证一次;Console.Write(object.ReferenceEquals(s3,s4));对于.NET 2.0,3.0,3.5,4.0,它返回true。此外,如果您测试s3=String.Intern(s3);Write(object.ReferenceEquals(s3,s1));可以看到s3=String.Intern(s3);什么都不做,因为像Scott Dorman一样,从s1到s3的所有数据都已经被掩埋,只有s4指向一个唯一的堆指针,然后我们用s4=String.Intern(s4)更改它;string.Interned()并不意味着传入的string对象是作为一个Interned字符串创建的,它意味着Interned存储中有一个具有相同值的字符串。令人困惑,哈!有道理。但是String.Intern(s4)没有插入字符串吗?是的,它会插入字符串,但是您仍然没有比较插入的引用。有关更多信息,请查看我答案的更新。从MSDN:
Intern方法使用Intern池搜索等于str值的字符串。如果存在此类字符串,则返回其在Intern池中的引用。如果
object arr   o1  == o2  ? False
string arr   o3  == o4  ? True
object const o5  == o6  ? True
string const o7  == o8  ? True
object cnsl  o9  == o10 ? False
string cnsl  o11 == o12 ? True
o1.Equals(o2) ? True
o3.Equals(o4) ? True
o5.Equals(o6) ? True
o7.Equals(o8) ? True
o9.Equals(o10) ? True
o11.Equals(o12) ? True