C# Почему при создании строки ключевое слово new можно не использовать?

Как известно, в C# для фактического создания объектов предназначено ключевое слово new. Но почему при создании строки это слово можно не использовать? Т.е. можно написать так:

string a = «привет»;

В MSDN смотрел, там написано: «Обратите внимание, что для создания строкового объекта оператор new не используется, за исключением случаев инициализации строки массивом символов».

Только учтите, меня интересуют не мотивы разработчиков, по которым они дали возможность опускать ключевое слово new, а как они это сделали (и как это можно сделать самому, для своего класса).

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
можно ответить намного короче: это синтаксический сахар.

при чём вполне уместный, хоть и рожает заблуждение в умах новичков что string это value type.

gif0, посмотрите все подробности например в MSDN. Если коротко, то у строки переопределены только операции == и! =. Конкатенация с помощью + это опять тот самый пресловутый syntactic sugar. В результате компилятор заменяет эту операцию на вызов

call       string [mscorlib]System.String::Concat(string, string)

Почему все так… Строки используются очень часто и поэтому вполне понятно, почему CLR с ними обращается не так, как с другими объектами — так более эффективно. По крайней мере мне хочется верить, что JIT-компилятор генерирует оптимальный машинный код для всех чисто строчных команд:)

ЗЫ. Троелсен — это хорошо, но ваши вопросы не всегда освещаются даже в более сложных книгах типа Рихтера, так что лучше всего будет все детали увидеть в IL-коде самому. ildasm и Reflector смогут вам в этом помочь:)

Сашка, я имею в виду, почему нет перезагрузки операторов «+», «-», «=» и т.п., а вместо них (перезагрузок) какие-то CIL инструкции, выполняющие те же функции (если я правильно понял bonifatio).

gif0, ну, а как такая перегрузка должна бы выглядеть по твоему мнению?
public static implicit operator string (string str)
{

return str; }

PomAH4uK, с чего вы взяли, что я чем-то недоволен? Просто я задумываюсь над тем, что изучаю.
>>> Може все таки варто спочатку щось базове вивчити
Я и изучаю «щось базове». Это не сложно понять с моего сообщения, которое вы процитировали.

Сашка, я понял, но почему для строки никакие перегрузки не используются?

Ну идеология скорее такая, что поскольку строками часто пользуются, то синтаксис их инициализации упростили. Как и для массивов кстати:

int [] numbers = {1, 2, 3, 4, 5}; // здесь тоже можно без new

Сашка, просто идеологию хочу понять

, я прочитал еще только 345 страниц книги Троелсена, и еще не знаю, что такое делегаты и т.п

почему для строки никакие перегрузки не используются? Разве это правильно, какой в этом смысл?

Може все таки варто спочатку щось базове вивчити, а тоді вже виявляти незадоволення чимось...

gif0, ты уже определись — то тебя не интересуют мотивы разработчиков, то вынь до положь тебе смысл:)

Зачем нужен какая-то перегрузка для string a = «привет»; если компилятору и так видно что строка в обеих частях выражения? Или ты уже про какой-то другой смысл интересуешься?

denis.gz, честно говоря, я прочитал еще только 345 страниц книги Троелсена, и еще не знаю, что такое делегаты и т.п. Но смысл сказанного я понял, спасибо.

bonifatio, почему для строки никакие перегрузки не используются? Разве это правильно, какой в этом смысл?

Да, для строки никакие перегрузки не используются. Для того кода, который я привел, в IL-коде появится дополнительный вызов вида

call       class StringImplicitConversion.MyClass StringImplicitConversion.MyClass::op_Implicit(string)

ЗЫ. StringImplicitConversion — это пространство имен

Всем спасибо за ответы.

bonifatio, отдельное спасибо за четкое объяснение. Как я понял, в С# можно не использовать ключевое слово new для объектов класса string, но не потому, что оператор «=» у него перезагружен, а из-за особых CIL инструкций?

Только учтите, меня интересуют не мотивы разработчиков, по которым они дали возможность опускать ключевое слово new, а как они это сделали (и как это можно сделать самому, для своего класса).

Вопрос в том, что именно Вы хотите, чтобы получилось? что именно дожно стоять справо от знака равно? В принципе, кое-что такое сделать можно, но смысла в этом я особо не вижу. Например, можно создать у класса implicit conversion operator из какого-нибудь простого типа данных, вроде строки и тогда можно будет писать что-то вроде:

MySuperClass a = "somestring";

Но, конечно, проблем тут не оберёшься, ибо все ошибки будут ловиться не компилятром, а в рантайм.

2Tim: Можно генерировать любые команды с помощью классов из пространства имен Reflection.Emit, вопрос только зачем:)

Упс, не заметил «меня интересуют не мотивы разработчиков». Наверное сами не сможете, нужны новые команды IL + чтоб компилятор понял что от него хотят. Возможно CLR и допускает возможость своего кастомайзинга, никогда таким не заморачивался.

1. Ага, у строк особый статус (неизменяемость, интернирование), поэтому в IL-коде вместо команды newobj используется ldstr, которая уже создает объект по метаданным, которые были добавлены на этапе компиляции.
2. Конструктор явно можно вызвать только для создания строки из массива символов (в том числе можно передать char*, если нужно работать с неуправляемым кодом), в остальных случаях нужно использовать инициализатор.

3. Если есть необходимость писать что-то вроде такого:

MyClass mc = "Hello, World!";

то вам нужно определить неявное преобразование. Например, вот так:

    public class MyClass
    {
        private readonly string _strVal;

public MyClass(string val)
        {
            _strVal = val;
        }

public static implicit operator MyClass(string str)
        {
            return new MyClass(str);
        }

public override string ToString()
        {
            return _strVal;
        }
    }

Для string предусмотрены специальные операторы IL, поэтому допускаются такие упрощения. Также при каждой операции с string возращается копия строки, то есть они неизменяемые. e.g.
string a = «aaa»;
string b = a + «bb»;
bool equal = object.ReferenceEquals (a, b);

equal будет false, так как обьекты «a» и «b» имеют разные адреса. Если нужно много манипуляций с string, лучше воспользоваться классом StringBuilder. Более полную инфу можете гялнуть в книге «CLR via C#» имхо одна из лучших книг по C# и.NET.

Я не очень разбираюсьь в C#, но такое у меня запустилось:

char [] arr = {’a’, ’c’};

string s = new String (arr);

Або в імплементації оператора = (TCHAR *source) просто копіювати вказівник (присвоювати полю цього класу String значення аргумента source), а при першій зміні об’єкта виділяти пам’ять з допомогою new, ну і звільняти в деструкторі delete-ом, якщо треба. Здається,

саме так це реалізовано в Delphi...

Не знаю, як це зроблено у C#-ному String, і взагалі не знаю C#, але знання C++ підказують, що таке можна реалізувати, якщо в імплементації перевизначеного оператора присвоєння міститься new, а в імплементації перегруженого деструктора — delete.

Підписатись на коментарі