簡體   English   中英

為什么在初始化字符串時不使用new運算符?

[英]Why don't we use new operator while initializing a string?

在一次采訪中有人問我這個問題:字符串是引用類型還是值類型。

我說它是參考類型。 然后他問我為什么在初始化字符串時不使用new運算符? 我說是因為c#語言具有用於創建字符串的更簡單的語法,並且編譯器自動將代碼轉換為對System.String類的構造函數的調用。

這個答案正確嗎?

字符串是不可變的引用類型。 ldstr IL指令允許將新的對象引用推送到字符串文字。 所以當你寫:

string a = "abc";

編譯器測試是否已在元數據中定義"abc"文字,如果未聲明,則進行測試。 然后,它將代碼轉換為以下IL指令:

ldstr "abc"

這基本上使a局部變量指向在元數據中定義的字符串文字。

所以我想說您的答案不太正確,因為編譯器沒有將其轉換為對構造函數的調用。

並非完全正確的答案。 字符串“特殊”引用類型。 他們是一成不變的。 沒錯,編譯器在內部執行某些操作,但這不是構造函數調用。 它調用ldstr ,它將新的對象引用推送到存儲在元數據中的字符串文字。

示例C#代碼:

class Program
{
    static void Main()
    {
        string str;
        string initStr = "test";
    }
}

這是IL代碼

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       8 (0x8)
  .maxstack  1
  .locals init ([0] string str,
           [1] string initStr)
  IL_0000:  nop
  IL_0001:  ldstr      "test"
  IL_0006:  stloc.1
  IL_0007:  ret
} // end of method Program::Main

您可以在上面看到ldstr調用。

甚至由於Strings的不可改變性,僅保留唯一/唯一的字符串成為可能。 所有字符串都保存在哈希表中 ,其中鍵是字符串值,而值是對該字符串的引用。 每當我們有一個新的字符串CLR檢查時,哈希表中就已經有這樣的字符串了。 如果沒有,則不分配新的內存,並將引用設置為此現有字符串。

您可以運行以下代碼進行檢查:

class Program
{
    static void Main()
    {
        string someString = "abc";
        string otherString = "efg";

        // will retun false
        Console.WriteLine(Object.ReferenceEquals(someString, otherString));

        someString = "efg";

        // will return true
        Console.WriteLine(Object.ReferenceEquals(someString, otherString));
    }
}    

好吧,編譯器具有簡化字符串創建的特殊語法是正確的。

關於編譯器生成對構造函數的調用的部分並不正確。 字符串文字是在應用程序啟動時創建的,因此在使用字符串文字的情況下,它只是對現有對象的引用的分配。

如果在循環中分配字符串文字:

string[] items = new string[10];
for (int i = 0; i < 10; i++) {
  items[i] = "test";
}

它不會為每次迭代創建新的字符串對象,而只是將相同的引用復制到每個項目中。

關於字符串文字的其他兩點值得注意的事情是,編譯器不會創建重復項,並且如果您將它們連接起來,它將自動將它們合並。 如果您多次使用同一文字字符串,它將使用同一對象:

string a = "test";
string b = "test";
string c = "te" + "st";

變量abc都指向同一對象。

字符串類還具有可以使用的構造函數:

string[] items = new string[10];
for (int i = 0; i < 10; i++) {
  items[i] = new String('*', 42);
}

在這種情況下,您實際上將獲得十個單獨的字符串對象。

不。 編譯器不會更改構造。 構造函數參數應為哪種類型? 串? ;-)

字符串文字是沒有名稱的常量。

此外,如果它支持運算符,則可以使用字符串文字初始化任何類:

   public class UnitTest1 {
      class MyStringable {
         public static implicit operator MyStringable(string value) {
            return new MyStringable();
         }
      }

      [TestMethod]
      public void MyTestMethod() {
         MyStringable foo = "abc";
      }
   }


編輯要更清楚:如您所問,如果將字符串轉換為任何構造函數調用,讓我們看一下IL代碼。

采取以下測試方法:

   [TestClass]
   class MyClass {
      [TestMethod]
      public void MyTest() {
         string myString = "foo";
         if (myString == "bar")
            Console.WriteLine("w00t");
      }
   }

創建以下IL代碼:

.method public hidebysig instance void MyTest() cil managed
{
    .custom instance void [Microsoft.VisualStudio.QualityTools.UnitTestFramework]Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute::.ctor()
    .maxstack 2
    .locals init (
        [0] string myString,
        [1] bool CS$4$0000)
    L_0000: nop 
    L_0001: ldstr "foo"
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: ldstr "bar"
    L_000d: call bool [mscorlib]System.String::op_Equality(string, string)
    L_0012: ldc.i4.0 
    L_0013: ceq 
    L_0015: stloc.1 
    L_0016: ldloc.1 
    L_0017: brtrue.s L_0024
    L_0019: ldstr "w00t"
    L_001e: call void [mscorlib]System.Console::WriteLine(string)
    L_0023: nop 
    L_0024: ret 
}

如您所見,所有字符串值(foo,bar和w00t)仍然是字符串,並且不會調用任何隱藏的構造函數。

希望這能進一步解釋。

眾所周知,字符串是不可變的,因此沒有隱式的構造函數調用。 我想為您添加以下參考,這可能會使您更加了解:

字符串不變性

但是我們可以在初始化字符串時使用new運算符

String str = new char[] {'s','t','r'};

這個答案正確嗎?

不,字符串被緩存和使用,可以像在IL中那樣說。

這是我的看法,我不太確定,因此請以一小撮鹽回答我。

.NET中的字符串文字是獨立的,其長度或其他數據結構內部包含在文字值本身中。 因此,與C不同,在.NET中分配字符串文字只是分配字符串整個數據結構的內存地址的問題。 在C語言中,我們需要使用new in string類,因為它需要圍繞以null終止的字符串(例如,長度)分配其他數據結構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM