Re: C# [Proposal] init block for safe initialization of complex
От: _FRED_ Черногория
Дата: 27.12.25 23:48
Оценка: +3 -1
Здравствуйте, VladD2, Вы писали:

VD>https://github.com/dotnet/csharplang/discussions/9903

VD>Прошу поддержать лайком по ссылке (на гитхебе). Ну и приветствуются замечания и предложения.

В целом кажется это можно решить анализатором кода без изменения компилятора:
Help will always be given at Hogwarts to those who ask for it.
Re[17]: C# [Proposal] init block for safe initialization of
От: _FRED_ Черногория
Дата: 27.12.25 23:38
Оценка: +3
Здравствуйте, Sinclair, Вы писали:

S>Это никак не поможет. Вы не понимаете главного: проблема не в null. А в отсутствии инициализации. У нас по ТЗ в графе узлов не должно быть "пустых" ссылок. Неважно, как эти пустые ссылки описываются — как null или как ссылка на специальный синглтон.


Вот почему мне изначальная идея Влада кажется бесперспективной: не очень понятно (точнее, усложнений потребует) передача init контекста. Например, если часть инициализиции мы захотим сделать в каком-либо методе (чтобы переиспользовать его). Придётся добавить init-модификатор к параметрам. Тогда как альтернатива в виде явного вызова метода валидации не так уж и страшна. А валидация сможет проверить не только то, что все ссылки проинициализтрованны "как-то", а и ещё, например, отсутствие циклов (если нужно) или что-то ещё.
Help will always be given at Hogwarts to those who ask for it.
Re[17]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 28.12.25 11:37
Оценка: :)))
Здравствуйте, Sinclair, Вы писали:


S>Это никак не поможет. Вы не понимаете главного: проблема не в null. А в отсутствии инициализации. У нас по ТЗ в графе узлов не должно быть "пустых" ссылок. Неважно, как эти пустые ссылки описываются — как null или как ссылка на специальный синглтон.


S>Вот как раз это и надо прогарантировать. И если бы не цикличность топологии, то всё неплохо бы работало и в существующем языке: required как раз и означает, что нужно обязательно проинициализировать эти свойства.


Это я прекрасно понимаю. Я просто для примера Влада высказал, для not null reference нужны заглушки типам Node.EmptyNode

Опять же ждя первой ноды должно быть Previous = Node.EmptyNode, а для последней Next = Node.EmptyNode
и солнце б утром не вставало, когда бы не было меня
C# [Proposal] init block for safe initialization of complex
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.12.25 14:12
Оценка: 5 (1) +1
https://github.com/dotnet/csharplang/discussions/9903

Прошу поддержать лайком по ссылке (на гитхебе). Ну и приветствуются замечания и предложения.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 23.12.2025 15:26 VladD2 . Предыдущая версия .
Re[18]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.12.25 03:25
Оценка: +2
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Sinclair, Вы писали:


S> Антон, а что смешного в


S>

S>для not null reference нужны заглушки типам Node.EmptyNode
S>Опять же для первой ноды должно быть Previous = Node.EmptyNode, а для последней Next = Node.EmptyNode


S> Мы делаем защиту от null, поэтому

То, что мы не делаем защиту от null. Мы делаем защиту от логических ошибок.
Один из видов логических ошибок — недоинициализация. Проблема не в null, а в том, что нужное свойство не получает осмысленного значения. Замена одного бессмысленного значения на другое — это шило на мыло.
Более того, во многих случаях такая замена делает решение хуже. Потому, что ещё сильнее откладывает детектирование проблемы. Null вызывает NRE при первом же обращении, а Empty стрельнет вообще непонятно где. Скорее всего — при проверке результатов работы, и то при условии достаточно внимательной проверки.
А мы хотим, чтобы ошибка обнаруживалась там, где она совершена. Соответственно, not null даст нам ошибку прямо там, где мы пытаемся засунуть null, да ещё и в компайл-тайм, а не при выполнении юнит-теста.
Empty такого преимущества лишён.


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

S>Мне больше нравится string.Empty так как в большинстве случаев приходится писать
S>
S>string.IsNullOrEmpty(str)
S>

S>вместо
S>
S>str.IsEmpty
S>

Во-первых, нет никакой разницы, что писать — IsNullOrEmpty или IsEmpty.
Если нам надо гарантировать, что в нужное место попадает URL файла, то ни null ни "" нас не устроят.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: C# [Proposal] init block for safe initialization of complex objects graph
От: rameel https://github.com/rsdn/CodeJam
Дата: 24.12.25 03:55
Оценка: 55 (1)
Здравствуйте, VladD2, Вы писали:

VD>Народ! Лайки нужны не здесь, а по ссылке.


Там в первую очередь проголосовал
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re: C# [Proposal] init block for safe initialization of comp
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.12.25 15:26
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>https://github.com/dotnet/csharplang/discussions/9903


VD>Прошу поддержать лайком. Ну и приветствуются замечания и предложения.


Народ! Лайки нужны не здесь, а по ссылке.

Да и даже не лайки, а поддержка. Там есть такая стрелочка вверх. Вот её нужно пржать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 30.12.2025 20:29 VladD2 . Предыдущая версия .
Re[8]: C# [Proposal] init block for safe initialization of complex
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.12.25 16:30
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S>угу с null ты получишь ошибку на null

И по крайней мере программа детерминированно упадёт. А с EmptyNode у нас может быть половина узлов в списке фиктивными, и программа будет делать вид, что работает. Вот только все метрики типа длин путей будут фуфельными.

S> и чем (list!=null) лучше

S>
S>while (!list.IsEmpty)
S>

Обратный вопрос: чем list.IsEmpty лучше, чем list == null?

S> Есть такая же польза как и у string.Empty. Позволяет работать с методами и свойствами.

Нет никакой пользы. Сосредоточьтесь и постарайтесь писать по делу, а не абстрактную чушь.

S> У него проблема с отложенной инициализацией. Но и в его примере для последнего узла Next будет null

И это должен отловить компилятор!

S> Ограничение может быть не только на Empty, но и по другим свойствам. И вот Empty как раз хорош.

Опять вы пишете что-то бессвязное.

S> Исходная задача это заменить null и добавить проверку на присваивание!

Нет. Исходная задача — это гарантировать, что при обработке графа никогда не встретится непроинициализированный узел
S> Это можно сделать и добавив свои анализаторы отключив родные.
S> Но в итоге и это не решает проблему ибо null то остается!
Ну так вам об этом и говорят.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 26.12.25 07:18
Оценка: :)
Здравствуйте, Sinclair, Вы писали:


S>> Это намного лучше чем городить новый язык, так как может применяться ко всем версиям языка начиная C# 8.0

S>В таком варианте можно попробовать. Хотя конечно меня радует ваше "Написать свой анализатор кода не проблема"

Просим дип сик и многое уже готово!
Ну там немного напильником подчистить
и солнце б утром не вставало, когда бы не было меня
Re[20]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.12.25 13:05
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S> Неее. Нам нужна проверка на полную инициализацию.

Всё верно.
S>И для первой ноды должно быть Previous = Node.EmptyNode, а для последней Next = Node.EmptyNode это осмысленное значение
Нет. С чего бы это "осмысленное значение"? Если нас устраивает, что у первой и последней вместо Previous и Next — какой-то булшит, то честнее там так и записать null.
По крайней мере, не будет иллюзии, что они во что-то там проинициализированы.

S> Но мы получаем проверку компилятора на инициализацию всех свойств.

Ничего мы не получаем. Получаем шиш с маслом. Вы можете сколько угодно подменять задачу, но это никак не приближает нас к решению исходной задачи.
Вы делаете, как мои дети: говоришь им "не должно быть мусора на полу" — ну так они его в одёжный шкаф запихивают. "Задача решена".

S>>Более того, во многих случаях такая замена делает решение хуже. Потому, что ещё сильнее откладывает детектирование проблемы. Nul


S> Зато у нас есть проверка на инициализацию свойств, что чего null reference лишен.

Нет никакого "зато". Есть иллюзия, что мы решили задачу. Это как claude, которой пишешь "такой-то тест падает — исправь". А она такая "а, ну да, точно, ведь наша реализация ещё не умеет обрабатывать массивы. Закомментируем тест — ура, все тесты зелёные!"
S>Ну а в большинстве проблем для Empty будет бесконечная рекурсия, которая тоже отлавливается.

По-моему, вы надо мной издеваетесь. Раньше нас беспокоила неполная инициализация, которая приводила к NRE при первом обращении. Мы хотели, чтобы неполнота инициализации отлавливалась раньше, желательно прямо в compile time.
Вы предложили заменить дефолтную значение с null на Empty, и теперь неполноту инициализации мы обнаруживаем не раньше, а позже, и ещё менее очевидным образом.
И ещё и настаиваете на своём решении
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[24]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.12.25 09:06
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S>Это просто набросок.


S>По уму нужно делать проверку на первый и последний узел который будет у родителя. А именно.


S>
S> void AddBeforeNode(Node node)
S>{
S>  if (node == FirstNode) // if (node.Previous  == Node.Empty) 
S>{
S>  var node = new Node(Node.Empty,node);
S>}
S> else 
S>{
S>// Вернее этот код будет работать и для первой ноды так как node.Previous == Node.Empty
S>  var node = new Node(node.Previous ,node);
S>}

S>node.Previous = node;
S>} 
S>


S>
S> void AddNodeToEnd()
S>{
S>  if (LastNode == node.Empty)
S>{
S>   LastNode = new Node(Node.Empty,Node.Empty);
S> FirstNode = LastNode; 
S>}
S>else
S>{
  
S>var node = new Node(LastNode ,LastNode.Next);
S>LastNode.Next = node;
S>LastNode = node;
 
S>}

S>}
S>

Что это вообще? Что за if? Что за методы? Кто такие FirstNode и LastNode?
Если вы не приучите себя к дисциплине написания связного текста на естественных языках и языках программирования, то в голове будет такая же каша, как в ваших постах в этом топике.

S>Нет не затыкает! Ибо для крайних узлов это не null!

Ну как же не затыкает — компилятор перестал жаловаться, а в коде по-прежнему ошибка.

S>Устал писать. Для последней ноды Next = Node.EmptyNode для первой Previous = Node.EmptyNode

Я и так это вижу. Вы, похоже, не понимаете, что в этом и есть проблема.
У нас НЕ БЫЛО задачи сделать "Для последней ноды Next = Node.EmptyNode для первой Previous = Node.EmptyNode". У нас была задача "обеспечить, чтобы у каждого узла были валидные Next и Previous узлы".
Вы заменили невалидный null (засунутый туда при помощи null! сквозь сжатые зубы компилятора) на невалидный Node.EmptyNode.
Пока вы не поймете, что вы так не решаете, а усугубляете проблему, толку не будет.

S>Значит будет смотреть все переменные в прагме, как это делается для всех прагм.

Что такое "все переменные в прагме"? Явно продекларированные? Все, включая темпоралы? Что насчёт неявно типизированных выражений вроде tuple initializer?

S>Возможно и стоит сделать для нового языка этот init, но прагма лучше подходит для старых языков. У многих стоит ограничение на фреймворки.

Прагму сначала нужно спроектировать. Влад, при всех недостатках его идеи, хотя бы внятно изложил а) проблему, б) предлагаемое решение.
У вас же решение на уровне "давайте придумаем какое-нибудь решение". Простите, это несерьёзный разговор. Предложите хоть мало-мальскую спецификацию поведения этой вашей прагмы.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[25]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.12.25 17:09
Оценка: :)
Здравствуйте, pilgrim_, Вы писали:

_>Сергей, такое ощущение что у тебя перед глазами стоит только не циклический двусвязный список, а теперь зацикли его, Как тут в примере у Антона:

+
_>
  Для наглядности возможные графы
_>Image: 5-cycle.png
_>Image: image001.png


Ну, в таком виде пентаграмма изоморфна обычному пятиугольнику (достаточно её распутать). Но никто не сказал, что node.Previous.Next обязательно == node
можно же сделать и так:
a.Next = b;
b.Next = c;
c.Next = d;
d.Next = e;
e.Next = a;
e.Previous = c;
d.Previous = b;
c.Previous = a;
b.Previous = e;
a.Previous = d;
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[26]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.12.25 17:26
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Sinclair, Вы писали:


S>С наступающим Новым Годом!

И вас так же
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[26]: C# [Proposal] init block for safe initialization of
От: pilgrim_ Россия  
Дата: 31.12.25 06:39
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Ну, в таком виде пентаграмма изоморфна обычному пятиугольнику (достаточно её распутать). Но никто не сказал, что node.Previous.Next обязательно == node

S>можно же сделать и так:
S>
S>a.Next = b;
S>b.Next = c;
S>c.Next = d;
S>d.Next = e;
S>e.Next = a;
S>e.Previous = c;
S>d.Previous = b;
S>c.Previous = a;
S>b.Previous = e;
S>a.Previous = d;
S>


Да как угодно, сорян что влез в ваш цикл, захотелось брекнуть с наступающим НГ!
Re: C# [Proposal] init block for safe initialization of complex
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 23.12.25 15:52
Оценка:
Здравствуйте, VladD2, Вы писали:

А у Node2.Next будет null?

Не проще ли инициализировать EmptyNode у которого будут ссылки на себя

и сделать приватный конструктор типа
this.Next = this;
this.Previous = this;

Ну и делать проверку на EmptyNode
и солнце б утром не вставало, когда бы не было меня
Re[2]: C# [Proposal] init block for safe initialization of complex
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.12.25 06:10
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Ну и делать проверку на EmptyNode

Это профанация идеи. Смысл not null — не в том, чтобы не получать NRE, а в том, чтобы иметь семантические гарантии.
EmptyNode ничуть не лучше null: во всём коде нужно делать проверки, что по ссылке — настоящая нода, а не эрзац.
Null Object Pattern работает тогда, когда у нас таки есть какой-то "нейтральный" объект, который может выступать в роли "заглушки". Ну, там, пустая строка вместо null-строки позволяет нам брать у неё длину, конкатенировать и т.п. вместо паники/исключения/segfault.
А non-nullability — это специальный инвариант, гарантирующий нетривиальную инициализацию.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: C# [Proposal] init block for safe initialization of complex
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.12.25 07:42
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>>Ну и делать проверку на EmptyNode

S>Это профанация идеи. Смысл not null — не в том, чтобы не получать NRE, а в том, чтобы иметь семантические гарантии.
S>EmptyNode ничуть не лучше null: во всём коде нужно делать проверки, что по ссылке — настоящая нода, а не эрзац.
S>Null Object Pattern работает тогда, когда у нас таки есть какой-то "нейтральный" объект, который может выступать в роли "заглушки". Ну, там, пустая строка вместо null-строки позволяет нам брать у неё длину, конкатенировать и т.п. вместо паники/исключения/segfault.
S>А non-nullability — это специальный инвариант, гарантирующий нетривиальную инициализацию.

Угу,

А у Node2.Next будет null?


Суть EmptyNode это как раз и есть аналог пустой строки. Ты можешь вызвать методы, свойства. Просто они будут ссылаться на соответствующие Empty.
То есть каждый класс должен создавать такой объект. При этом он должен быть иммутабельным.
и солнце б утром не вставало, когда бы не было меня
Re[4]: C# [Proposal] init block for safe initialization of complex
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.12.25 09:58
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Суть EmptyNode это как раз и есть аналог пустой строки. Ты можешь вызвать методы, свойства. Просто они будут ссылаться на соответствующие Empty.

S> То есть каждый класс должен создавать такой объект. При этом он должен быть иммутабельным.

Зачем вы объясняете мне то же самое, что я вам объяснил?
Null object pattern не решает поставленную задачу.
Вот представьте, что у вас есть департаменты, в которых работают сотрудники.
И есть инвариант, что у каждого департамента обязан быть указан руководитель, а у каждого сотрудника — департамент.
Не в том, чтобы они были "не null"! А в том, что не может быть департамента без руководителя, и сотрудника вне департамента.

Минимальная корректная топология — это department1.Manager = employee1, и employee1.Department = department1.
Нет никакого "EmptyDepatment" — кто у него будет руководителем? И нет никакого EmptyEmployee — в каком департаменте он будет работать?
Даже если мы возьмём и "как-то" породим эту минимальную пару, обойдя ограничение языка, нам это никак не поможет.
Потому что мы сможем сконструировать, например, департамент departmentX = new Department() { Manager = EmptyEmployee}.
И этот департамент с точки зрения бизнес-логики ничуть не лучше, чем департамент, у которого .Manager == null.
Не может "никто" быть руководителем департамента — бизнес-логика такая!
Мы, собственно, хотим убрать изо всего кода бесчисленных проверок вида if(dept.Manager == null) log.Warning("Обнаружен департамент без руководителя — не можем подготовить приказ о начислении премий") else RegisterBonus(dept.Manager). Всё, что вы можете добиться с NullObject — это замены этого кода на if(dept.Manager == EmptyEmployee) log.Warning("Обнаружен департамент без руководителя — не можем подготовить приказ о начислении премий") else RegisterBonus(dept.Manager)
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: C# [Proposal] init block for safe initialization of complex
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.12.25 10:11
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>> Суть EmptyNode это как раз и есть аналог пустой строки. Ты можешь вызвать методы, свойства. Просто они будут ссылаться на соответствующие Empty.

S>> То есть каждый класс должен создавать такой объект. При этом он должен быть иммутабельным.

S>Зачем вы объясняете мне то же самое, что я вам объяснил?

S>Null object pattern не решает поставленную задачу.
S>Вот представьте, что у вас есть департаменты, в которых работают сотрудники.
S>И есть инвариант, что у каждого департамента обязан быть указан руководитель, а у каждого сотрудника — департамент.
S>Не в том, чтобы они были "не null"! А в том, что не может быть департамента без руководителя, и сотрудника вне департамента.

S>Минимальная корректная топология — это department1.Manager = employee1, и employee1.Department = department1.

S>Нет никакого "EmptyDepatment" — кто у него будет руководителем? И нет никакого EmptyEmployee — в каком департаменте он будет работать?
S>Даже если мы возьмём и "как-то" породим эту минимальную пару, обойдя ограничение языка, нам это никак не поможет.
S>Потому что мы сможем сконструировать, например, департамент departmentX = new Department() { Manager = EmptyEmployee}.
S>И этот департамент с точки зрения бизнес-логики ничуть не лучше, чем департамент, у которого .Manager == null.
S>Не может "никто" быть руководителем департамента — бизнес-логика такая!
S>Мы, собственно, хотим убрать изо всего кода бесчисленных проверок вида if(dept.Manager == null) log.Warning("Обнаружен департамент без руководителя — не можем подготовить приказ о начислении премий") else RegisterBonus(dept.Manager). Всё, что вы можете добиться с NullObject — это замены этого кода на if(dept.Manager == EmptyEmployee) log.Warning("Обнаружен департамент без руководителя — не можем подготовить приказ о начислении премий") else RegisterBonus(dept.Manager)


Sinclair не читатель. Еще раз какой узел следующий для последнего узла?
Empty нужны для обозначения, что этот объект дефолтный. У него можно вызвать методы, получить свойства.
Так же его нельзя присваивать свойствам которые должны иметь недефолтные значения и это проверяется в сеттерах или конструкторах.
Можно помечать такие свойства атрибутами и проверять через SG
Все тоже, что и со string.Empty
и солнце б утром не вставало, когда бы не было меня
Re[6]: C# [Proposal] init block for safe initialization of complex
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.12.25 10:48
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Sinclair не читатель.

Нет, это Serginio1 не читатель.

S>Еще раз какой узел следующий для последнего узла?

Зависит от используемых инвариантов. Например, у "последнего" узла следующим может быть null.
Это позволяет, к примеру, делать простое итерирование:
public static IEnumerable<Node> EnumerateForward(Node? list) {
  while (list) {
    yield return list;
    list = list.Next;
  }
}

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

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

S> Empty нужны для обозначения, что этот объект дефолтный. У него можно вызвать методы, получить свойства.

Я вам в третий раз пишу: дефолтные объекты не помогают решить проблему, сформулированную Владом. У него проблема не в том, что у null нельзя вызывать методы или получать свойства.
S>Так же его нельзя присваивать свойствам которые должны иметь недефолтные значения и это проверяется в сеттерах или конструкторах.
Прекрасно. Попробуйте запретить присваивание EmptyNode в сеттерах Next и Previous. Получите нерабочий код.
И вообще — вы переносите проверку в рантайм, а смысл не в том, чтобы неверный код падал при исполнении. Он и так падает при исполнении.
И делать проверки на null в конструкторе мы умели ещё четыре версии языка назад:
this.next = next ?? throw new ArgumentNullException(nameof(next))


S>Можно помечать такие свойства атрибутами и проверять через SG

Это по-прежнему не решает исходную задачу.
S> Все тоже, что и со string.Empty
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: C# [Proposal] init block for safe initialization of complex
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.12.25 11:07
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>Sinclair не читатель.

S>Нет, это Serginio1 не читатель.

S>>Еще раз какой узел следующий для последнего узла?

S>Зависит от используемых инвариантов. Например, у "последнего" узла следующим может быть null.
S>Это позволяет, к примеру, делать простое итерирование:
S>
S>public static IEnumerable<Node> EnumerateForward(Node? list) {
S>  while (list) {
S>    yield return list;
S>    list = list.Next;
S>  }
S>}
S>

S>Это работает, если для нас такая топология является нормальной. В такой топологии никакой пользы от EmptyNode нету.


угу с null ты получишь ошибку на null
и чем (list!=null) лучше
while (!list.IsEmpty)





S>Если нет, то у нас совсем другой инвариант — за нодой должна идти валидная нода. И в ней тоже нет никакой пользы от EmptyNode.


Есть такая же польза как и у string.Empty. Позволяет работать с методами и свойствами.

S>> Empty нужны для обозначения, что этот объект дефолтный. У него можно вызвать методы, получить свойства.

S>Я вам в третий раз пишу: дефолтные объекты не помогают решить проблему, сформулированную Владом. У него проблема не в том, что у null нельзя вызывать методы или получать свойства.

У него проблема с отложенной инициализацией. Но и в его примере для последнего узла Next будет null

S>>Так же его нельзя присваивать свойствам которые должны иметь недефолтные значения и это проверяется в сеттерах или конструкторах.

S>Прекрасно. Попробуйте запретить присваивание EmptyNode в сеттерах Next и Previous. Получите нерабочий код.


S>И вообще — вы переносите проверку в рантайм, а смысл не в том, чтобы неверный код падал при исполнении. Он и так падает при исполнении.

S>И делать проверки на null в конструкторе мы умели ещё четыре версии языка назад:
S>
S>this.next = next ?? throw new ArgumentNullException(nameof(next))
S>


Ограничение может быть не только на Empty, но и по другим свойствам. И вот Empty как раз хорош.

S>>Можно помечать такие свойства атрибутами и проверять через SG

S>Это по-прежнему не решает исходную задачу.
S>> Все тоже, что и со string.Empty
S>

Исходная задача это заменить null и добавить проверку на присваивание!
Это можно сделать и добавив свои анализаторы отключив родные.
Но в итоге и это не решает проблему ибо null то остается!
и солнце б утром не вставало, когда бы не было меня
Re: C# [Proposal] init block for safe initialization of complex
От: sergii.p  
Дата: 24.12.25 14:27
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>https://github.com/dotnet/csharplang/discussions/9903


init (var obj1 = new SomeType1(), var obj2 = new SomeType2())
{
obj1.Prop = obj2;
obj2.Prop = obj1;
// Local functions are allowed
void Connect() => obj1.Child = obj2;
Connect();
} // Compiler checks here that all non-nullable members are initialized

почему только локальные функции? Как бы чего не вышло? Установка свойства уже может вызывать кучу логики вполне себе внешней.

Да и пока не очень понял, так ли уж оно сильно надо? Например, чем плохо использовать обычные reference types? Или использовать нормальные Optional (которых правда в шарпе нет, но их можно подтянуть извне). Н-р почти во всех языках Next будет Optional/Maybe.
Вообще motivation часть как-то не раскрыта.
Re[9]: C# [Proposal] init block for safe initialization of c
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.12.25 17:29
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>Обратный вопрос: чем list.IsEmpty лучше, чем list == null?


Тем, что кой кому не нужен Nullable reference


S>> Исходная задача это заменить null и добавить проверку на присваивание!

S>Нет. Исходная задача — это гарантировать, что при обработке графа никогда не встретится непроинициализированный узел
S>> Это можно сделать и добавив свои анализаторы отключив родные.
S>> Но в итоге и это не решает проблему ибо null то остается!
S>Ну так вам об этом и говорят.

Но в итоге у него код не пройдет проверку! Так как Next для последнего и Previous для первого будет null!
Никто не мешает добавить свой анализатор без изменения языка!
А использование Empty объекта значительно лучше чем null!
и солнце б утром не вставало, когда бы не было меня
Отредактировано 24.12.2025 17:38 Serginio1 . Предыдущая версия . Еще …
Отредактировано 24.12.2025 17:32 Serginio1 . Предыдущая версия .
Re[10]: C# [Proposal] init block for safe initialization of c
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.12.25 02:05
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Тем, что кой кому не нужен Nullable reference

А вам не приходило в голову задуматься: а почему этому кой-кому не нужен Nullable Reference? Если не приходило — перечитайте стартовый пост, там всё написано.

S> Но в итоге у него код не пройдет проверку! Так как Next для последнего и Previous для первого будет null!

Именно это и прекрасно. Компилятор потребует от него добавить инициализацию для этих свойств, и только тогда успокоится. Это и есть статическая гарантия того, что в рантайме он никогда не напорется ни на Null, ни на EmptyNode.
S>Никто не мешает добавить свой анализатор без изменения языка!
Ок, предположим, у нас есть такой анализатор. Накидайте пример пользовательского кода с его применением.

S>А использование Empty объекта значительно лучше чем null!

Нет. Восклицательные знаки аргументом в споре не являются. Повторюсь: я знаком с Null Object Pattern и понимаю не только его преимущества, но и недостатки.
В данной задаче этот паттерн противопоказан как раз тем, что мешает компилятору проверять инициализированность. Потому что понятие "Ссылка на Node, для которой запрещено использование null" в компилятор встроено, а "ссылка на Node, для которой запрещено использование EmptyNode" — нет. Это не Алгол.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: C# [Proposal] init block for safe initialization of complex
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.12.25 02:13
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>почему только локальные функции? Как бы чего не вышло?

Потому, что про нелокальные функции у компилятора нет информации. Я и с локальными-то сомневаюсь, что
Установка свойства уже может вызывать кучу логики вполне себе внешней.

SP>Да и пока не очень понял, так ли уж оно сильно надо? Например, чем плохо использовать обычные reference types?

avoiding unexpected nulls is a huge benefit, particularly in larger codebases where it's harder to keep track of the edge cases where things can be null by yourself

SP>Или использовать нормальные Optional (которых правда в шарпе нет, но их можно подтянуть извне). Н-р почти во всех языках Next будет Optional/Maybe.
Попробуйте переписать пример с использованием Optional. Станет хуже
SP>Вообще motivation часть как-то не раскрыта.
По-моему так вполне внятно раскрыта. Что именно непонятно или вызывает сомнения?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.12.25 07:07
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>> Тем, что кой кому не нужен Nullable reference

S> А вам не приходило в голову задуматься: а почему этому кой-кому не нужен Nullable Reference? Если не приходило — перечитайте стартовый пост, там всё написано.

S>> Но в итоге у него код не пройдет проверку! Так как Next для последнего и Previous для первого будет null!

S>Именно это и прекрасно. Компилятор потребует от него добавить инициализацию для этих свойств, и только тогда успокоится. Это и есть статическая гарантия того, что в рантайме он никогда не напорется ни на Null, ни на EmptyNode.
Антон ты прикалываешься? Еще раз

Так как Next для последнего и Previous для первого будет null


Только добавив Empty компилятор и успокоится
S>>Никто не мешает добавить свой анализатор без изменения языка!
S>Ок, предположим, у нас есть такой анализатор. Накидайте пример пользовательского кода с его применением.

#продвинутый nullable enable
        var obj1 = new SomeType1();
       var obj2 = new SomeType2();
obj1.Prop = obj2;
obj2.Prop = obj1;
// Local functions are allowed
void Connect() => obj1.Child = obj2;
Connect();
#продвинутый nullable disable


Ну или
#pragma warning disable продвинутый nullable
#pragma warning restore продвинутый nullable

через наследника DiagnosticAnalyzer
и солнце б утром не вставало, когда бы не было меня
Отредактировано 25.12.2025 10:48 Serginio1 . Предыдущая версия . Еще …
Отредактировано 25.12.2025 10:18 Serginio1 . Предыдущая версия .
Отредактировано 25.12.2025 9:35 Serginio1 . Предыдущая версия .
Re[3]: C# [Proposal] init block for safe initialization of complex
От: sergii.p  
Дата: 25.12.25 10:41
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Потому, что про нелокальные функции у компилятора нет информации.


то есть вызвать нелокальные функции в обработке property.set он может, а в init блоке он их уже не видит? Или может я не понял, что имеется в виду под локальными функциями? Локальные в модуле, классе, функции?

S>Попробуйте переписать пример с использованием Optional. Станет хуже


переписал. Не вижу, что стало хуже

using System;
using System.Collections.Generic;
using Optional;

public class Node<T>
{
    public required T Value { get; set; }
    public Option<Node<T>> Next { get; set; }
    public Option<Node<T>> Previous { get; set; }

    public Node(T value)
    {
        Value = value;
        Next = Option.None<Node<T>>();
        Previous = Option.None<Node<T>>();
    }

    public static Option<Node<T>> Init<T>(IEnumerable<T> values)
    {
        using var enumerator = values.GetEnumerator();
        if (!enumerator.MoveNext())
            return Option.None<Node<T>>();

        var head = new Node<T>(enumerator.Current);
        var current = head;

        while (enumerator.MoveNext())
        {
            var newNode = new Node<T>(enumerator.Current);

            current.Next = Option.Some(newNode);
            newNode.Previous = Option.Some(current);

            current = newNode;
        }

        return Option.Some(head);
    }

    public static IEnumerable<T> TraverseForward<T>(Option<Node<T>> start)
    {
        var current = start;

        while (current.Match(
            some: node =>
            {
                yield return node.Value;
                current = node.Next;
                return true;
            },
            none: () => false
        )) { }
    }
}


S>Что именно непонятно или вызывает сомнения?


ну вот привёл код выше. Чем секция init будет лучше? Позволит писать компактнее — спорно. Безопаснее — да нисколько. В общем motivation должен быть в виде сравнения. Вот у меня был такой workaround, а вот так получается с init блоком. Второй вариант очевидно лучше по следующим причинам ... Сейчас лично мне совсем не очевидно.
Re[12]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.12.25 15:18
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Антон ты прикалываешься? Еще раз

S>

S>Так как Next для последнего и Previous для первого будет null

Ок. Я, наверное, плохо объясняю очевидные лично мне вещи.
Давайте помедленнее. Вот у нас класс ноды с обязательными ссылками:

class Node
{
    public required Node Next { get; set; }
    public required Node Previous { get; set; }
    public string Name {get; init;}
    public Node(string name) => Name = name;
}

public static IEnumerable<string> Iterate(Node start) {
  var current = start;
  do { 
    yield return current.Name; 
    current = current.Next;
  } while(current != start);
}


Существующий компилятор, попытка 1:
var node1 = new Node("Hello"); 
var node2 = new Node("World") { Next = node1, Previous = node1 }; 
foreach(var n in Iterate(node1))
  Console.WriteLine(n);

Compile time error: Required member 'Program.Node.Next' must be set in the object initializer or attribute constructor.
Compile time error: Required member 'Program.Node.Previous' must be set in the object initializer or attribute constructor.


попытка 2:
var node1 = new Node("Hello") { Next = null!, Previous = null! }; 
var node2 = new Node("World") { Next = node1, Previous = node1) }; 
node1.Previous = node2;
foreach(var n in Iterate(node1)
  Console.WriteLine(n);

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

Попробуйте решить проблему при помощи EmptyNode.


S>
S>#продвинутый nullable enable
S>        var obj1 = new SomeType1();
S>       var obj2 = new SomeType2();
S>obj1.Prop = obj2;
S>obj2.Prop = obj1;
S>// Local functions are allowed
S>void Connect() => obj1.Child = obj2;
S>Connect();
S>#продвинутый nullable disable
S>

Ничего не получится. Если Prop — non-nullable required, то этот код не скомпилируется. Если он объявлен как nullable — то любой случайный дятел сможет его занулить после строчки #продвинутй nullable disable.
Ещё идеи есть?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.12.25 15:27
Оценка:
Здравствуйте, Sinclair, Вы писали:

решить проблему при помощи EmptyNode.


S>>
S>>#продвинутый nullable enable
S>>        var obj1 = new SomeType1();
S>>       var obj2 = new SomeType2();
S>>obj1.Prop = obj2;
S>>obj2.Prop = obj1;
S>>// Local functions are allowed
S>>void Connect() => obj1.Child = obj2;
S>>Connect();
S>>#продвинутый nullable disable
S>>

S>Ничего не получится. Если Prop — non-nullable required, то этот код не скомпилируется. Если он объявлен как nullable — то любой случайный дятел сможет его занулить после строчки #продвинутй nullable disable.
S>Ещё идеи есть?

Еще раз #продвинутый nullable enable это анализатор проверки установки свойств
Он будет выдавать ошибку если не установлены все свойства obj1 и obj2
Написать свой анализатор кода не проблема.
Разумеется основная проверка non-nullable required будет отключена, так за проверку будет отвечать #продвинутый nullable enable

Если нет отмены то можно и так

 #nullable disable
 #продвинутй nullable enable
 код c проверкой на присваивание всем полям не null значений
 #продвинутй nullable disable
 #nullable enable


Это намного лучше чем городить новый язык, так как может применяться ко всем версиям языка начиная C# 8.0
и солнце б утром не вставало, когда бы не было меня
Отредактировано 25.12.2025 19:56 Serginio1 . Предыдущая версия . Еще …
Отредактировано 25.12.2025 19:53 Serginio1 . Предыдущая версия .
Re[13]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.12.25 15:40
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>попытка 2:

S>
S>var node1 = new Node("Hello") { Next = null!, Previous = null! }; 
S>var node2 = new Node("World") { Next = node1, Previous = node1) }; 
S>node1.Previous = node2;
S>foreach(var n in Iterate(node1)
S>  Console.WriteLine(n);
S>

S>Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

S>Попробуйте решить проблему при помощи EmptyNode.



var node1 = new Node("Hello") { Next = Node.EmptyNode, Previous = Node.EmptyNode }; 
var node2 = new Node("World") { Next = node1, Previous = Node.EmptyNode) }; 
node1.Previous = node2;
foreach(var n in Iterate(node1)
Console.WriteLine(n);


И никаких System.NullReferenceException
и солнце б утром не вставало, когда бы не было меня
Re[2]: C# [Proposal] init block for safe initialization of complex
От: VladD2 Российская Империя www.nemerle.org
Дата: 25.12.25 23:01
Оценка:
Здравствуйте, sergii.p, Вы писали:

Э
SP>почему только локальные функции?

Потому что их проще проанализировать. Они локализованы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: C# [Proposal] init block for safe initialization of c
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.12.25 05:30
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>то есть вызвать нелокальные функции в обработке property.set он может

Не очень понял, что вы имеете в виду. Можно пример?
SP>Или может я не понял, что имеется в виду под локальными функциями? Локальные в модуле, классе, функции?
В примере это локальная функция, объявленная по месту. См. Example 2.

S>>Попробуйте переписать пример с использованием Optional. Станет хуже


SP>переписал. Не вижу, что стало хуже


SP>
SP>using System;
SP>using System.Collections.Generic;
SP>using Optional;

SP>public class Node<T>
SP>{
SP>    public required T Value { get; set; }
SP>    public Option<Node<T>> Next { get; set; }
SP>    public Option<Node<T>> Previous { get; set; }

SP>    public Node(T value)
SP>    {
SP>        Value = value;
SP>        Next = Option.None<Node<T>>();
SP>        Previous = Option.None<Node<T>>();
SP>    }

SP>    public static Option<Node<T>> Init<T>(IEnumerable<T> values)
SP>    {
SP>        using var enumerator = values.GetEnumerator();
SP>        if (!enumerator.MoveNext())
SP>            return Option.None<Node<T>>();

SP>        var head = new Node<T>(enumerator.Current);
SP>        var current = head;

SP>        while (enumerator.MoveNext())
SP>        {
SP>            var newNode = new Node<T>(enumerator.Current);

SP>            current.Next = Option.Some(newNode);
SP>            newNode.Previous = Option.Some(current);

SP>            current = newNode;
SP>        }

SP>        return Option.Some(head);
SP>    }

SP>    public static IEnumerable<T> TraverseForward<T>(Option<Node<T>> start)
SP>    {
SP>        var current = start;

SP>        while (current.Match(
SP>            some: node =>
SP>            {
SP>                yield return node.Value;
SP>                current = node.Next;
SP>                return true;
SP>            },
SP>            none: () => false
SP>        )) { }
SP>    }
SP>}
SP>

Это означает, что логику проверки на Some/None нужно тащить по всему коду. Если уж этим задуряться, то проверки на null компактнее и лучше читаются:
using System;
using System.Collections.Generic;

public class Node<T>
{
    public required T Value { get; set; }
    public Node<T>? Next { get; set; }
    public Node<T>? Previous { get; set; }

    public Node(T value)
    {
        Value = value;
    }

    public static Node<T>? Init<T>(IEnumerable<T> values)
    {
        using var enumerator = values.GetEnumerator();
        if (!enumerator.MoveNext())
            return null;

        var head = new Node<T>(enumerator.Current);
        var current = head;

        while (enumerator.MoveNext())
        {
            var newNode = new Node<T>(enumerator.Current);

            current.Next = newNode;
            newNode.Previous = current;

            current = newNode;
        }

        return Option.Some(head);
    }

    public static IEnumerable<T> TraverseForward<T>(Node<T>? start)
    {
        var current = start;

        while (current)
        {
             yield return node.Value;
             current = node.Next;
        }
    }
}


SP>ну вот привёл код выше. Чем секция init будет лучше? Позволит писать компактнее — спорно. Безопаснее — да нисколько.

У вас в коде Init ошибка. Секция init будет лучше тем, что обнаружит её на этапе компиляции.
SP>В общем motivation должен быть в виде сравнения. Вот у меня был такой workaround, а вот так получается с init блоком. Второй вариант очевидно лучше по следующим причинам ... Сейчас лично мне совсем не очевидно.
Вроде такой motivation как раз и приведён.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 26.12.2025 5:35 Sinclair . Предыдущая версия .
Re[14]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.12.25 05:32
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Еще раз #продвинутый nullable enable это анализатор проверки установки свойств

S>Он будет выдавать ошибку если не установлены все свойства obj1 и obj2
S>Написать свой анализатор кода не проблема.
S>Разумеется основная проверка non-nullable required будет отключена, так за проверку будет отвечать #продвинутый nullable enable
Глобальное отключение non-nullable подорвёт всю идею.
S>Если нет отмены то можно и так

S>
S> #nullable disable
S> #продвинутй nullable enable
S> код c проверкой на присваивание всем полям не null значений
S> #продвинутй nullable disable
S> #nullable enable
S>


S> Это намного лучше чем городить новый язык, так как может применяться ко всем версиям языка начиная C# 8.0

В таком варианте можно попробовать. Хотя конечно меня радует ваше "Написать свой анализатор кода не проблема"
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.12.25 05:35
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Sinclair, Вы писали:



S>>попытка 2:

S>>
S>>var node1 = new Node("Hello") { Next = null!, Previous = null! }; 
S>>var node2 = new Node("World") { Next = node1, Previous = node1) }; 
S>>node1.Previous = node2;
S>>foreach(var n in Iterate(node1)
S>>  Console.WriteLine(n);
S>>

S>>Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

S>>Попробуйте решить проблему при помощи EmptyNode.



S>
S>var node1 = new Node("Hello") { Next = Node.EmptyNode, Previous = Node.EmptyNode }; 
S>var node2 = new Node("World") { Next = node1, Previous = Node.EmptyNode) }; 
S>node1.Previous = node2;
S>foreach(var n in Iterate(node1)
S>Console.WriteLine(n);
S>


S>И никаких System.NullReferenceException

Во-первых, вы сначала напишите код Node.EmptyNode
У нас проблема не в NullReferenceException, а в нарушении инварианта. В частности, node1.Next и node2.Previous так и остались непроинициализированными. Вы код-то попробуйте запустить
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 26.12.25 07:22
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>>Попробуйте решить проблему при помощи EmptyNode.



S>>
S>>var node1 = new Node("Hello") { Next = Node.EmptyNode, Previous = Node.EmptyNode }; 
S>>var node2 = new Node("World") { Next = node1, Previous = Node.EmptyNode) }; 
S>>node1.Previous = node2;
S>>foreach(var n in Iterate(node1)
S>>Console.WriteLine(n);
S>>


S>>И никаких System.NullReferenceException

S>Во-первых, вы сначала напишите код Node.EmptyNode
S>У нас проблема не в NullReferenceException, а в нарушении инварианта. В частности, node1.Next и node2.Previous так и остались непроинициализированными. Вы код-то попробуйте запустить

Я же уже писал

private Node(bool blin)
{
 this._next= this;
 this._previous = this;
}


Ну и в Next и Previous запрет на изменения для EmptyNode.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 26.12.2025 7:31 Serginio1 . Предыдущая версия .
Re[16]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 27.12.25 06:24
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Sinclair, Вы писали:


S>>>>Попробуйте решить проблему при помощи EmptyNode.


S>>У нас проблема не в NullReferenceException, а в нарушении инварианта. В частности, node1.Next и node2.Previous так и остались непроинициализированными. Вы код-то попробуйте запустить


S> Я же уже писал


S>
S>private Node(bool blin)
S>{
S> this._next= this;
S> this._previous = this;
S>}
S>

Это никак не поможет. Вы не понимаете главного: проблема не в null. А в отсутствии инициализации. У нас по ТЗ в графе узлов не должно быть "пустых" ссылок. Неважно, как эти пустые ссылки описываются — как null или как ссылка на специальный синглтон.
И вот ваш код нарушает этот инвариант!
node1.Next == Node.EmptyNode
node1.Previous == node2
node2.Next == node1
node2.Previous == Node.EmptyNode

А должно быть (например)
node1.Next == node2
node1.Previous == node2
node2.Next == node1
node2.Previous == node1

Вот как раз это и надо прогарантировать. И если бы не цикличность топологии, то всё неплохо бы работало и в существующем языке: required как раз и означает, что нужно обязательно проинициализировать эти свойства.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 28.12.25 18:05
Оценка:
Здравствуйте, Sinclair, Вы писали:

Антон, а что смешного в

для not null reference нужны заглушки типам Node.EmptyNode
Опять же для первой ноды должно быть Previous = Node.EmptyNode, а для последней Next = Node.EmptyNode


Мы делаем защиту от null, поэтому


public Node<T>? Next { get; set; }
    public Node<T>? Previous { get; set; }


Не подходят, так как нужны только для крайних узлов. Во всех других случаях нужно делать проверку инициализации.

Или отключать проверку

#nullable disable
#nullable enable

И продвинутый анализатор тоже будет ругаться на крайние узлы.
Мне больше нравится string.Empty так как в большинстве случаев приходится писать
string.IsNullOrEmpty(str)

вместо
str.IsEmpty
и солнце б утром не вставало, когда бы не было меня
Re[19]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.12.25 07:32
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>> Антон, а что смешного в


S>>

S>>для not null reference нужны заглушки типам Node.EmptyNode
S>>Опять же для первой ноды должно быть Previous = Node.EmptyNode, а для последней Next = Node.EmptyNode


S>> Мы делаем защиту от null, поэтому

S>То, что мы не делаем защиту от null. Мы делаем защиту от логических ошибок.
S>Один из видов логических ошибок — недоинициализация. Проблема не в null, а в том, что нужное свойство не получает осмысленного значения. Замена одного бессмысленного значения на другое — это шило на мыло.

Неее. Нам нужна проверка на полную инициализацию.
И для первой ноды должно быть Previous = Node.EmptyNode, а для последней Next = Node.EmptyNode это осмысленное значение
Но мы получаем проверку компилятора на инициализацию всех свойств.

S>Более того, во многих случаях такая замена делает решение хуже. Потому, что ещё сильнее откладывает детектирование проблемы. Null вызывает NRE при первом же обращении, а Empty стрельнет вообще непонятно где. Скорее всего — при проверке результатов работы, и то при условии достаточно внимательной проверки.


S>А мы хотим, чтобы ошибка обнаруживалась там, где она совершена. Соответственно, not null даст нам ошибку прямо там, где мы пытаемся засунуть null, да ещё и в компайл-тайм, а не при выполнении юнит-теста.

S>Empty такого преимущества лишён.

Зато у нас есть проверка на инициализацию свойств, что чего null reference лишен.
Ну а в большинстве проблем для Empty будет бесконечная рекурсия, которая тоже отлавливается.
и солнце б утром не вставало, когда бы не было меня
Re[21]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.12.25 14:51
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>> Неее. Нам нужна проверка на полную инициализацию.

S>Всё верно.
S>>И для первой ноды должно быть Previous = Node.EmptyNode, а для последней Next = Node.EmptyNode это осмысленное значение
S>Нет. С чего бы это "осмысленное значение"? Если нас устраивает, что у первой и последней вместо Previous и Next — какой-то булшит, то честнее там так и записать null.
S>По крайней мере, не будет иллюзии, что они во что-то там проинициализированы.



S>> Но мы получаем проверку компилятора на инициализацию всех свойств.

S>Ничего мы не получаем. Получаем шиш с маслом. Вы можете сколько угодно подменять задачу, но это никак не приближает нас к решению исходной задачи.
S>Вы делаете, как мои дети: говоришь им "не должно быть мусора на полу" — ну так они его в одёжный шкаф запихивают. "Задача решена".

Получаем. Ибо для всех нод кроме первой и последней у нас будет проверка, а с null у нас этой проверки не будет.
Либо нужно делать

#nullable disable
#nullable enable

S>>>Более того, во многих случаях такая замена делает решение хуже. Потому, что ещё сильнее откладывает детектирование проблемы. Nul

S>> Зато у нас есть проверка на инициализацию свойств, что чего null reference лишен.

S>Нет никакого "зато". Есть иллюзия, что мы решили задачу. Это как claude, которой пишешь "такой-то тест падает — исправь". А она такая "а, ну да, точно, ведь наша реализация ещё не умеет обрабатывать массивы. Закомментируем тест — ура, все тесты зелёные!"

Еще раз читай внимательно для всех узлов кроме первой и последней будет "правильная "проверка, чего лишен Node?

S>>Ну а в большинстве проблем для Empty будет бесконечная рекурсия, которая тоже отлавливается.

S>
S>По-моему, вы надо мной издеваетесь. Раньше нас беспокоила неполная инициализация, которая приводила к NRE при первом обращении. Мы хотели, чтобы неполнота инициализации отлавливалась раньше, желательно прямо в compile time.
S>Вы предложили заменить дефолтную значение с null на Empty, и теперь неполноту инициализации мы обнаруживаем не раньше, а позже, и ещё менее очевидным образом.
S>И ещё и настаиваете на своём решении

Меня беспокоило то, что в алгоритме Влада будет вызываться ошибка, так как первый и последний узел не будут инициализироваться!
Я предложил , что не нужно изменять язык, достаточно добавить свой анализатор типа

 #nullable disable
 #продвинутй nullable enable
 код c проверкой на присваивание всем полям не null значений
 #продвинутй nullable disable
 #nullable enable



Ну или

 #nullable disable
 #pragma warning restore продвинутый nullable
 код c проверкой на присваивание всем полям не null значений
 #pragma warning disable продвинутый nullable
 #nullable enable
и солнце б утром не вставало, когда бы не было меня
Re[22]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.12.25 15:03
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Получаем. Ибо для всех нод кроме первой и последней у нас будет проверка, а с null у нас этой проверки не будет.

Нет. Ни для какой из нод никакой проверки нет.
То, что вы кому-то из них что-то установили, никак не контролируется компилятором.

Вот код — ошибка копипаста:
var node1 = new Node("Hello") { Next = Node.EmptyNode, Previous = Node.EmptyNode }; 
var node2 = new Node("World") { Next = node1, Previous = Node.EmptyNode) }; 
node1.Previous = node2;
foreach(var n in Iterate(node1)
  Console.WriteLine(n);

Компилятор молчит. Дальше что? Где проверка-то?

S> Еще раз читай внимательно для всех узлов кроме первой и последней будет "правильная "проверка, чего лишен Node?

Сколько бы раз я не читал, неправда правдой быть не перестанет. Проверка либо есть, либо её нету. EmptyNode проверку затыкает — ничуть не лучше, чем null!.

S>Меня беспокоило то, что в алгоритме Влада будет вызываться ошибка, так как первый и последний узел не будут инициализироваться!

Ну так и в вашем коде они не инициализируются.

S>Я предложил , что не нужно изменять язык, достаточно добавить свой анализатор типа


Ну так Влад примерно это и предлагает. Только встроить "анализатор" в компилятор, где уже процентов 70% этого анализа проведено. Плюс есть ещё несколько интересных вопросов про то, как должен этот анализатор выяснять, что именно ему нужно проверить. Влад пытался навелосипедить специальный стейтмент, который позволяет указать границы "опасной зоны" вместе с конкретными объектами, которые нужно доинициализировать в её пределах. А как прагма поможет понять, за кем следить? Что, если прагмы будут стоять не по границам C# scope?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[23]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.12.25 15:26
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>> Получаем. Ибо для всех нод кроме первой и последней у нас будет проверка, а с null у нас этой проверки не будет.

S>Нет. Ни для какой из нод никакой проверки нет.
S>То, что вы кому-то из них что-то установили, никак не контролируется компилятором.

S>Вот код — ошибка копипаста:

S>
S>var node1 = new Node("Hello") { Next = Node.EmptyNode, Previous = Node.EmptyNode }; 
S>var node2 = new Node("World") { Next = node1, Previous = Node.EmptyNode) }; 
S>node1.Previous = node2;
S>foreach(var n in Iterate(node1)
S>  Console.WriteLine(n);
S>

S>Компилятор молчит. Дальше что? Где проверка-то?


Это просто набросок. По уму нужно делать проверку на первый и последний узел который будет у родителя. А именно.


 void AddBeforeNode(Node node)
{
  if (node == FirstNode) // if (node.Previous  == Node.Empty) 
{
  var node = new Node(Node.Empty,node);
}
 else 
{
// Вернее этот код будет работать и для первой ноды так как node.Previous == Node.Empty
  var node = new Node(node.Previous ,node);
  node.Previous.Next = node;
}

node.Previous = node;
}


 void AddNodeToEnd()
{
  if (LastNode == node.Empty)
{
   LastNode = new Node(Node.Empty,Node.Empty);
 FirstNode = LastNode; 
}
else
{
  
var node = new Node(LastNode ,LastNode.Next);
LastNode.Next = node;
LastNode = node;
 
}

}



S>> Еще раз читай внимательно для всех узлов кроме первой и последней будет "правильная "проверка, чего лишен Node?

S>Сколько бы раз я не читал, неправда правдой быть не перестанет. Проверка либо есть, либо её нету. EmptyNode проверку затыкает — ничуть не лучше, чем null!.
Нет не затыкает! Ибо для крайних узлов это не null!
S>>Меня беспокоило то, что в алгоритме Влада будет вызываться ошибка, так как первый и последний узел не будут инициализироваться!
S>Ну так и в вашем коде они не инициализируются.

Устал писать. Для последней ноды Next = Node.EmptyNode для первой Previous = Node.EmptyNode

S>var node2 = new Node("World") { Next = node1, Previous = Node.EmptyNode


S>>Я предложил , что не нужно изменять язык, достаточно добавить свой анализатор типа


S>Ну так Влад примерно это и предлагает. Только встроить "анализатор" в компилятор, где уже процентов 70% этого анализа проведено. Плюс есть ещё несколько интересных вопросов про то, как должен этот анализатор выяснять, что именно ему нужно проверить. Влад пытался навелосипедить специальный стейтмент, который позволяет указать границы "опасной зоны" вместе с конкретными объектами, которые нужно доинициализировать в её пределах. А как прагма поможет понять, за кем следить? Что, если прагмы будут стоять не по границам C# scope?


Значит будет смотреть все переменные в прагме, как это делается для всех прагм.

Возможно и стоит сделать для нового языка этот init, но прагма лучше подходит для старых языков. У многих стоит ограничение на фреймворки.
и солнце б утром не вставало, когда бы не было меня
Отредактировано 30.12.2025 9:10 Serginio1 . Предыдущая версия . Еще …
Отредактировано 29.12.2025 15:50 Serginio1 . Предыдущая версия .
Re[24]: C# [Proposal] init block for safe initialization of
От: pilgrim_ Россия  
Дата: 29.12.25 16:39
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Нет не затыкает! Ибо для крайних узлов это не null!

S>Устал писать. Для последней ноды Next = Node.EmptyNode для первой Previous = Node.EmptyNode

Сергей, такое ощущение что у тебя перед глазами стоит только не циклический двусвязный список, а теперь зацикли его, Как тут в примере у Антона:

node1.Next == node2
node1.Previous == node2
node2.Next == node1
node2.Previous == node1


  Для наглядности возможные графы



Влад в том числе о таких циклах и говорил как я понял (на гитхабе правда пример не дописанный для узлов, но из контекста вроде всё понятно).
Re[25]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.12.25 17:37
Оценка:
Здравствуйте, pilgrim_, Вы писали:


S>>Нет не затыкает! Ибо для крайних узлов это не null!

S>>Устал писать. Для последней ноды Next = Node.EmptyNode для первой Previous = Node.EmptyNode


_>Влад в том числе о таких циклах и говорил как я понял (на гитхабе правда пример не дописанный для узлов, но из контекста вроде всё понятно).


Это классический пример двухнаправленного списка. Или двоичных деревьев.

С полностью циклическими графами я дело не имел. Всегда есть тупики.

У него пример как раз

init (var root = new TreeNode(), var child1 = new TreeNode(), var child2 = new TreeNode())
{
    root.Left = child1;
    root.Right = child2;
    
    SetParent(child1, root);
    SetParent(child2, root);
    
    void SetParent(TreeNode child, TreeNode parent)
    {
        child.Parent = parent; // Parent is a non-nullable property
    }
}


Где для child Left и Right?

Сlass Node
{
    public required Node Next { get; set; }
    public required Node Previous { get; set; }
}

init (var node1 = new Node(), var node2 = new Node())
{
    node1.Next = node2;
    node2.Previous = node1;
}


где для node2.Next и node1.Previous ?


Опять же варианты кода деревья пустые без узлов?
и солнце б утром не вставало, когда бы не было меня
Отредактировано 29.12.2025 18:02 Serginio1 . Предыдущая версия . Еще …
Отредактировано 29.12.2025 17:45 Serginio1 . Предыдущая версия .
Отредактировано 29.12.2025 17:42 Serginio1 . Предыдущая версия .
Отредактировано 29.12.2025 17:38 Serginio1 . Предыдущая версия .
Re[25]: C# [Proposal] init block for safe initialization of
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 30.12.25 09:09
Оценка:
Здравствуйте, Sinclair, Вы писали:

С наступающим Новым Годом!
и солнце б утром не вставало, когда бы не было меня
Re[2]: C# [Proposal] init block for safe initialization of complex
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.12.25 12:15
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>В целом кажется это можно решить анализатором кода без изменения компилятора:

_FR>* Тип SomeType должен быть отмечен неким атрибутом
_FR>* Свойства/поля/параметры/возвращаемые значения методов так же, там где это необходимо, размечаются атрибутами, говорящими, что объекты/значения в них могут быть ещё только частично проинициализированны
_FR>* Как только переменная типа SomeType покидает такой вот "частично проинициализированный" контекст (например возвращается из метода, возвращаемое значение которого атрибутом не размечено), анализатор может проверить, что тип полностью инициализирован.

Ты предлагаешь полностью заместить NRT (Nullable Reference Types) и реализовать собственный анализ потока управления? Т.е. создать параллельную вселенную с закатом солнца вручную?

И тебя не напрягает, что ты не сможешь в коде использовать NRT и required?

Тогда зачем вообще нужен NRT?

А сложность такой системы ты точно правильно оцениваешь?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[27]: C# [Proposal] init block for safe initialization of
От: Sinclair Россия https://github.com/evilguest/
Дата: 02.01.26 05:22
Оценка:
Здравствуйте, pilgrim_, Вы писали:

_>Да как угодно, сорян что влез в ваш цикл, захотелось брекнуть с наступающим НГ!

Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: C# [Proposal] init block for safe initialization of complex
От: _FRED_ Черногория
Дата: 04.01.26 11:02
Оценка:
Здравствуйте, VladD2, Вы писали:

_FR>>В целом кажется это можно решить анализатором кода без изменения компилятора:


VD>Ты предлагаешь полностью заместить NRT (Nullable Reference Types) и реализовать собственный анализ потока управления? Т.е. создать параллельную вселенную с закатом солнца вручную?

VD>И тебя не напрягает, что ты не сможешь в коде использовать NRT и required?
VD>Тогда зачем вообще нужен NRT?

Будет похоже, да, но это не будет "замещение".

И снова, мы же не нуллабельность, кажется, обсуждаем, а логику инициализации, то есть required — то, что должно работать не только с NRT, но и с любым типом.

VD>А сложность такой системы ты точно правильно оцениваешь?


Мне не кажется, что это сложнее NRT. Позиция же разработчиков языка/компилятора, заключающаяся в том, что если что-то можно сделать анализатором/генератором, то так делать и следует, в целом мне понятна.

Давай лучше подумаем, что, кроме сложности, мешает сделать соответствующий анализатор? Поддержка в языке для полноценной картины, потребует нового модификатора для параметра (C# [Proposal] init block for safe initialization of).
Help will always be given at Hogwarts to those who ask for it.
Re[4]: C# [Proposal] init block for safe initialization of complex
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.01.26 17:18
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Будет похоже, да, но это не будет "замещение".


Нет. Это будет полностью параллельная NRT система. Иначе просто не получается.

_FR>И снова, мы же не нуллабельность, кажется, обсуждаем, а логику инициализации, то есть required — то, что должно работать не только с NRT, но и с любым типом.


Ты явно не разобрался в вопросе. Ключевое слово required — это часть NRT.

Ты не можешь сделать какие-то проверки в анализаторе и при этом параллельно использовать NRT, а required это её часть.

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

_FR>Мне не кажется, что это сложнее NRT.


Это 💯 сложнее, так как это расширение NRT, и.е. NRT + дополнительная логика. Отказавшись от NRT, ты вынужден будешь воспроизвести NRT вручную.

Исходная реализация NRT присутствующая в компиляторе не позволит тебе оставить объект недоинициализированным.

_FR>Позиция же разработчиков языка/компилятора, заключающаяся в том, что если что-то можно сделать анализатором/генератором, то так делать и следует, в целом мне понятна.


Это только в твоих фантазиях можно сделать анализатором. На практике это будет нагромождение костылей.

Нормальная реализация должна быть частью NRT.

Ну или надо выбрасывать NRT и всю логику городить на атрибутах и анализаторе.

И, кстати, тебя ни сколько не смущает, что NRT можно было сделать в виде анализатора?

_FR>Давай лучше подумаем, что, кроме сложности, мешает сделать соответствующий анализатор? Поддержка в языке для полноценной картины, потребует нового модификатора для параметра (C# [Proposal] init block for safe initialization of).


Подумай. Ты явно сделал это очень поверхностно. Я как человек занимавшихся компиляторами, понимаю, что качественно это можно сделать только в рамках подсистемы NRT.

Возьми мои примеры. Попробуй записать их как ты предлагаешь. Попробуй скомпилировать это текущим компилятором. Приведи их здесь если что-то получится.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.