Как стать автором
Обновить

Комментарии 33

Почему 7.2 (а не скажем 8)? Где ссылки на первоисточник инфы? Как по мне — не до конца разжевано, многое остается «за кадром», не уверен, что мое «додумывание» верно.

Например, именование параметров при вызове возможно необходимо, чтобы при рефакторинге получить ошибки по всех вызовах где позиция не совпадет? А зачем?

Про подчеркивание не понял вообще, что за проблему решили.
Чуть-чуть изменил про подчеркивание, надеюсь стало понятнее. И добавил источник. Спасибо за замечания )
Не нашел в официальной документации упоминания о типах Span T, Memory T — подскажите пожалуйста откуда взята информация?
Например, именование параметров при вызове возможно необходимо, чтобы при рефакторинге получить ошибки по всех вызовах где позиция не совпадет?

Рефакторинг не может приводить к ошибкам, иначе это не рефакторинг по определению рефакторинга. :)


Именованные параметры полезны для самокомментирования. Например, ниже два варианта вызова одного и того же метода. Чтобы понять первый вариант, надо помнить или посмотреть сигнатуру метода, а второй вариант понятен сразу.


ShowMessage(true, null, "Hello, world!");
ShowMessage(modal: true, parentWindow: null, "Hello, world!");

Про подчеркивание не понял вообще, что за проблему решили.

Двоичные и шестнадцатеричные литералы лучше смотрятся, когда цифры отделены от обязательного префикса. Два последних варианта нагляднее, но раньше были невозможны:


0xfedcba09
0xfedc_ba09
0x_fedcba09
0x_fedc_ba09

Странно, что сразу так не сделали, когда вводили разделители в 7.0.

Спасибо за ответ. Действительно литералы так «красивше».

А про сигнатуру метода все равно не понял, но я и раньше не особо-то использовал именованные параметры, скажем, исключительно если много из них со значенями по умолчанию (в конце списка параметров) и мне хочется только один передать:
MyMessageBox("Bla", "Error", icon: Icon.Error);
Уж получше чем указывать значения, равные тем, что по-умолчанию, для всех предыдущих параметров:
MyMessageBox("Bla", "Error", Buttons.Ok, Sounds.Beep, Parent.Center, Icon.Error);
В чем именно «соль» использования параметров без имени после именованых — не понятно. В вашем примере первый вариант самый правильный. Посмотреть сигнатуру метода (если нужно) можно простым мышконаведением в нормальной IDE, а каждый раз писать второй вариант — перебор.
Посмотреть сигнатуру метода (если нужно) можно простым мышконаведением в нормальной IDE, а каждый раз писать второй вариант — перебор.

Ну просто это лишние телодвижения, да и код может смотреться не из IDE, а из гитхаба через браузер.


Последний пример, кстати, отлично демонстрирует пользу именованных аргументов, т.к. у первых двух непонятно, кто из них caption, а кто message. Следующий вариант такой проблемы не имеет:


MyMessageBox(caption: "Bla", message: "Error", Buttons.Ok, Sounds.Beep, Parent.Center, Icon.Error);

Но раньше так писать было нельзя, пришлось бы подписывать имена всем последующим аргументам, хотя их назначение и так до безобразия очевидно.

НЛО прилетело и опубликовало эту надпись здесь
Честно говоря, когда видишь в заголовке Span и Memory, хочется увидеть в статье несколько больше информации о них, чем 1 абзац на двоих. Предыдущие новинки разжеваны и с примерами, а с ними скомкано, словно вам под конец надоело писать. Прям кликбейтом запахло.
Особенно это актуально для объемных структур хранимых в стеке, например массивов. Представьте себе рекурсивный вызов метода, с передачей в качестве аргумента массива из 1 000 000 элементов…


Исходя из: msdn.microsoft.com/en-us/library/bb985948.aspx массив не хранится в стеке. На мой взгляд здесь есть неточность.
Arrays are mechanisms that allow you to treat several items as a single collection. The Microsoft® .NET Common Language Runtime (CLR) supports single-dimensional arrays, multidimensional arrays, and jagged arrays (arrays of arrays). All array types are implicitly derived from System.Array, which itself is derived from System.Object. This means that all arrays are always reference types which are allocated on the managed heap, and your app's variable contains a reference to the array and not the array itself.

Да, моя ошибка. Уже исправил. Спасибо )
Хранимые по значению — простые типы данных, такие как int или bool, которые хранятся напрямую в стеке

Но ведь это миф, значимые типы хранятся по месту создания, стало быть где угодно.
Про Span уже сказали, тоже ожидал больше подробностей.
В данном контексте имеется ввиду хранение параметров методов и локальных переменных.
Зачем разделили понятия const и readonly? Ведь можно было const ref и все в таком духе, а не городить ключевые слова за так.
Понятия const и readonly в языке C# разделены начиная с самой первой версии. И описанное поведение — явный readonly.
В этом и заключается вопрос)

Ключевое слово in в языке уже было. Использовалось в паре с out для обозначения ковариантности и контрвариантности. И внутри foreach ещё.


Для передачи параметров по ссылке использовались ключевые слова ref (значение параметра может быть изменено) и out (значение параметра должно быть изменено). Теперь есть третий вариант передачи по ссылке — in (значение параметра не может быть изменено). Между двумя вариантами, использовать 'in' или 'readonly ref', выбрали 'in' для краткости и смысловой симметрии с 'out'.

Да-да-да, но я задавал вопрос о дизайне: почему именно in и readonly, вместо const и const ref. Интересно было бы услышать версии, почему создатели Java и C# стараются избегать const в широком смысле, как это было в C.

Константа в C# отличается от readonly переменной тем, что вычисляется во время компиляции и вычисленное значение подставляется вместо имени константы везде, где это имя упоминается. Readonly-переменнная — это хоть и readonly, но всё-таки переменная, т.е. именованное место в памяти, где лежит какое-то значение.


У константы значение может быть только одно — захардкоженное в исходнике. Значение readonly переменной может быть любым. Оно вычисляется в рантайме, каждый раз может оказываться разным и может зависеть от чего-угодно, хоть от фазы Луны. Т.е. это явно не константа.


Пример константы — число пи. Пример readonly-переменной — id объекта, он у всех разный, присваивается один раз во время создания объекта и никогда не меняется.

Ну и если подобную логику разделения на const и readonly в C# применить к параметрам (без оглядки на другие языки), то в варианте с использованием ключевого слова const получается глупость:


void Foo(const float x)

Выглядит как передача константы в метод, но у констант значения никогда не меняются. Этот параметр просто лишний.


void Foo(ref const float x)

Ссылка на значение константы? Смотри предыдущий пункт.


void Foo(const ref float x)

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

Какая польза для программиста в отличиях const и readonly? И те, и другие менять нельзя, вне зависимости от этапа создания значения. Есть ли какие-то кейсы (кроме создания самих const-переменных, конечно), при которых эта разница играла бы роль на уровне языка?

Бонус-вопрос: почему интерполированные строки не могут быть const, а конкатенированные — могут?
почему интерполированные строки не могут быть const, а конкатенированные — могут?

Это следует из той самой разницы между readonly и const на уровне языка, о которой уже было написано выше. const — определяется во время компиляции, а readonly — во время исполнения. Интерполяция строк трансформируется компилятором в вызов String.Format, что по определению уже не compile time. И да, не все конкатенированные строки могут быть const.
const — определяется во время компиляции, а readonly — во время исполнения
Мне нужен кейс, где это полезно (для чего так сделали?), а не констатация, что это так.

Интерполяция строк трансформируется компилятором в вызов String.Format, что по определению уже не compile time
В дополнение к предыдущему пункту: компилятор прекрасно справляется с конкатенацией const-значений, а интерполяцию (тех же const-значений) в компайл-тайме он почему-то не осиливает.

Ну-ну, интерполируйте-ка вот это: $"{1000.67}"

В том, что в зависимости от значения Thread.CurrentThread.CurrentCulture это может быть одно из следующих значений: "1000.67", "1000,67", "1,000.67", "1.000,67", "1 000.67", "1 000,67" (список может быть неполным).

Хорошо, этот случай не работает и для конкатенации. Как насчет строк-констант?
const string a = "Hello";
const string b = " World!";
const string c = a + b; // OK
// const string c = $"{a}{b}"; // error
Попробую начать сначала.
const — псевдоним литерала, т.е. используя псевдоним вы реально используете литерал. Например,
const a = "Hello";
string b = a;

является аналогом
string b = "Hello";

readonly — объявлет переменную и в этом принципиальная разница с const. Из-за этой разницы не рекомендуется использовать публичные (public) константы, т.к. при изменении значения публичной константы, необходимо перекомпилировать все сборки, которые эту константу использовали. Если этого не сделать, то часть модулей будет использовать старое значение, а перекомпилированные модули будут использовать уже новое. Отличный способ нарваться на проблемы. readonly поля такой особенностью не обладают, т.к. для них выделяется необходимая область памяти и уже туда кладется значение или ссылка.
Что касается интерполяции строк, то это синтаксический сахар для замены не самого удобного в использовании string.Format. Как я уже написал ранее const это псевдоним литерала, а вызов string.Format не является литералом, т.к. результат можно получить только во время выполнения и он зависит, например, от региональных настроек системы.
Что касается интерполяции строк, то это синтаксический сахар для замены не самого удобного в использовании string.Format
Скажите просто, что вы не знаете объективных причин реализовать интерполяцию строк такого рода (где никакие региональные настройки, фаза луны и прочие вещи, которые почему-то не мешают конкатенации) в компайл-тайме и мы торжественно закроем этот тредик!
не реализовывать*
void Foo(const float x)
Выглядит как передача константы в метод, но у констант значения никогда не меняются. Этот параметр просто лишний.
const — как признак неизменности объекта, вне зависимости от его природы. Но даже по вашей (в смысле текущей сишарпной) логике, ведь мы можем передавать различные константы, почему же нет?
void Foo(const ref float x)
Если это константная ссылка (грубо, адрес в памяти), то значит, адрес там всегда один и тот же, всегда указывает на один и тот же объект. Тоже хрень какая-то.
Тогда как насчет readonly ref?
const — как признак неизменности объекта, вне зависимости от его природы. Но даже по вашей (в смысле текущей сишарпной) логике, ведь мы можем передавать различные константы, почему же нет?

По сишарпной логике, если мы передаём в метод разные значения, то x не константа. Значения-то разные, а у константы значение всегда одно. В сишарпной логике этот x не const, а readonly — может быть инициализирован разными значениями, но менять полученное значение нельзя.


Тогда как насчет readonly ref?

Ссылка на объект, который менять можно, а саму ссылку перенаправить на другой объект нельзя. Разница с обычным модификатором ref мне неочевидна. Может и есть, но надо вдумываться.


По идее, ссылка на объект, который нельзя изменять, должна обозначаться как ref readonly. Для результатов и локальных ссылок оно ровно так и пишется:


ref readonly float Foo() { }

ref readonly float x = ref Foo();

Для параметров выбирали из вариантов ref readonly и in, выбрали in. Это такое же старое ключевое слово, оно намного короче, дополняет out и описывает смысл, а не реализацию:
in — ссылка для чтения входных данных
out — ссылка для записи выходных данных
ref — просто ссылка, делай что хочешь

Span и Memory не добавлены в язык. Они добавлены отдельным nuget пакетом и могут использоваться с любой версией C#. Другое дело, что в CLR для них добавили специальную поддержку.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории