Подтвердить что ты не робот

Почему мы не используем новый оператор при инициализации строки?

Мне задали этот вопрос в интервью: Является ли строка ссылочным типом или типом значения.

Я сказал, что это ссылочный тип. Затем он спросил меня, почему мы не используем новый оператор при инициализации строки? Я сказал, потому что язык С# имеет более простой синтаксис для создания строки, и компилятор автоматически преобразует код в вызов для конструктора класса System.String.

Правильно ли это ответ?

4b9b3361

Ответ 1

Строки - это неизменные ссылочные типы. Там ldstr инструкция IL, которая позволяет нажимать ссылку на новый объект на строковый литерал. Поэтому, когда вы пишете:

string a = "abc";

Компилятор проверяет, был ли литерал "abc" уже определен в метаданных, и если он не объявлен. Затем он переводит этот код в следующую инструкцию IL:

ldstr "abc"

Как правило, локальная переменная a указывает на строковый литерал, определенный в метаданных.

Итак, я бы сказал, что ваш ответ не совсем прав, поскольку компилятор не переводит это в вызов конструктора.

Ответ 2

Не совсем правильный ответ. Строки являются "специальными" ссылочными типами. Они неизменны. Вы правы, что компилятор делает что-то внутренне, но это не вызов конструктора. Он вызывает ldstr, который выталкивает новую ссылку на объект в строковый литерал, хранящийся в метаданных.

Пример кода С#:

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 выше.

Еще больше из-за неизменности строк можно сохранить только отдельные/уникальные строки. Все строки хранятся в хеш-таблице, где ключ является строковым значением, а значение является ссылкой на эту строку. Каждый раз, когда у нас есть новая строковая проверка 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));
    }
}    

Ответ 3

Ну, это правильно, что компилятор имеет специальный синтаксис, который упрощает создание строк.

Часть о компиляторе, вызывающая вызов конструктора, на самом деле неверна. Строковые литералы создаются при запуске приложения, поэтому там, где литерал строки используется, это только назначение ссылки на уже существующий объект.

Если вы назначаете строковый литерал в цикле:

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";

Переменные a, b и c все указывают на один и тот же объект.

В классе строк также есть конструкторы, которые вы можете использовать:

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

В этом случае вы фактически получите десять отдельных строковых объектов.

Ответ 4

Неа. Компилятор не меняет конструкцию. Каким должен быть аргумент конструктора? Строка?; -)

Строковые литералы - это константы без имени.

Кроме того, вы можете инициализировать любой класс строковым литералом, если он поддерживает оператор:

   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) по-прежнему являются строками и не вызывают никакого скрытого конструктора.

Надеюсь, что это больше объясняет.

Ответ 5

Как говорят все, строка неизменна, поэтому вызов конструктора неявно. Я хотел бы добавить следующую ссылку для вас, которая может немного очистить воздух:

Нерушимость строк

Ответ 6

Но мы можем использовать новый оператор, чтобы инициализировать строку

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

Правильно ли это ответ?

Нет, строка кэшируется и используется, пусть говорят, что есть в IL.

Ответ 7

Здесь мой прием, я не совсем уверен, поэтому отвечай мой ответ с солью.

Строковые литералы в .NET являются автономными, его длина или другая структура данных внутренне включается в собственно литеральное значение. Таким образом, в отличие от C, назначение строкового литерала в .NET - это просто назначение адреса памяти всей структуры данных строки. В C нам нужно использовать новый в строчном классе, так как ему нужно выделить другие структуры данных вокруг строки с нулевым завершением, например длина.