В целом кажется это можно решить анализатором кода без изменения компилятора:
Тип SomeType должен быть отмечен неким атрибутом
Свойства/поля/параметры/возвращаемые значения методов так же, там где это необходимо, размечаются атрибутами, говорящими, что объекты/значения в них могут быть ещё только частично проинициализированны
Как только переменная типа SomeType покидает такой вот "частично проинициализированный" контекст (например возвращается из метода, возвращаемое значение которого атрибутом не размечено), анализатор может проверить, что тип полностью инициализирован.
Help will always be given at Hogwarts to those who ask for it.
Re[17]: C# [Proposal] init block for safe initialization of
Здравствуйте, 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
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
Здравствуйте, 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
Здравствуйте, 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
S>> Это намного лучше чем городить новый язык, так как может применяться ко всем версиям языка начиная C# 8.0 S>В таком варианте можно попробовать. Хотя конечно меня радует ваше "Написать свой анализатор кода не проблема"
Просим дип сик и многое уже готово!
Ну там немного напильником подчистить
и солнце б утром не вставало, когда бы не было меня
Re[20]: C# [Proposal] init block for safe initialization of
Здравствуйте, 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
Здравствуйте, 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>
Что это вообще? Что за 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
Здравствуйте, pilgrim_, Вы писали: _>Сергей, такое ощущение что у тебя перед глазами стоит только не циклический двусвязный список, а теперь зацикли его, Как тут в примере у Антона:
+ _>
Ну, в таком виде пентаграмма изоморфна обычному пятиугольнику (достаточно её распутать). Но никто не сказал, что node.Previous.Next обязательно == node
можно же сделать и так:
Здравствуйте, Sinclair, Вы писали:
S>Ну, в таком виде пентаграмма изоморфна обычному пятиугольнику (достаточно её распутать). Но никто не сказал, что node.Previous.Next обязательно == node S>можно же сделать и так: S>
Здравствуйте, 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
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
Здравствуйте, 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
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
Здравствуйте, 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
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>>Sinclair не читатель. S>Нет, это Serginio1 не читатель.
S>>Еще раз какой узел следующий для последнего узла? S>Зависит от используемых инвариантов. Например, у "последнего" узла следующим может быть null. 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
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
S>Обратный вопрос: чем list.IsEmpty лучше, чем list == null?
Тем, что кой кому не нужен Nullable reference
S>> Исходная задача это заменить null и добавить проверку на присваивание! S>Нет. Исходная задача — это гарантировать, что при обработке графа никогда не встретится непроинициализированный узел S>> Это можно сделать и добавив свои анализаторы отключив родные. S>> Но в итоге и это не решает проблему ибо null то остается! S>Ну так вам об этом и говорят.
Но в итоге у него код не пройдет проверку! Так как Next для последнего и Previous для первого будет null!
Никто не мешает добавить свой анализатор без изменения языка!
А использование Empty объекта значительно лучше чем null!
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, 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
Здравствуйте, 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
Здравствуйте, 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 allowedvoid Connect() => obj1.Child = obj2;
Connect();
#продвинутый nullable disable
Здравствуйте, 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
Здравствуйте, 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
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
и солнце б утром не вставало, когда бы не было меня
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
Здравствуйте, 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 как раз и приведён.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, 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
Здравствуйте, 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
Здравствуйте, 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 так и остались непроинициализированными. Вы код-то попробуйте запустить
Здравствуйте, Serginio1, Вы писали:
S>Здравствуйте, Sinclair, Вы писали:
S>>>>Попробуйте решить проблему при помощи EmptyNode.
S>>У нас проблема не в NullReferenceException, а в нарушении инварианта. В частности, node1.Next и node2.Previous так и остались непроинициализированными. Вы код-то попробуйте запустить
S> Я же уже писал
S>
Это никак не поможет. Вы не понимаете главного: проблема не в null. А в отсутствии инициализации. У нас по ТЗ в графе узлов не должно быть "пустых" ссылок. Неважно, как эти пустые ссылки описываются — как null или как ссылка на специальный синглтон.
И вот ваш код нарушает этот инвариант!
Вот как раз это и надо прогарантировать. И если бы не цикличность топологии, то всё неплохо бы работало и в существующем языке: required как раз и означает, что нужно обязательно проинициализировать эти свойства.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: C# [Proposal] init block for safe initialization of
для 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
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
Здравствуйте, 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
Здравствуйте, 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
Здравствуйте, 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.Emptyvar 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, но прагма лучше подходит для старых языков. У многих стоит ограничение на фреймворки.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали: S>Нет не затыкает! Ибо для крайних узлов это не null! S>Устал писать. Для последней ноды Next = Node.EmptyNode для первой Previous = Node.EmptyNode
Сергей, такое ощущение что у тебя перед глазами стоит только не циклический двусвязный список, а теперь зацикли его, Как тут в примере у Антона:
Здравствуйте, _FRED_, Вы писали:
_FR>В целом кажется это можно решить анализатором кода без изменения компилятора: _FR>* Тип SomeType должен быть отмечен неким атрибутом _FR>* Свойства/поля/параметры/возвращаемые значения методов так же, там где это необходимо, размечаются атрибутами, говорящими, что объекты/значения в них могут быть ещё только частично проинициализированны _FR>* Как только переменная типа SomeType покидает такой вот "частично проинициализированный" контекст (например возвращается из метода, возвращаемое значение которого атрибутом не размечено), анализатор может проверить, что тип полностью инициализирован.
Ты предлагаешь полностью заместить NRT (Nullable Reference Types) и реализовать собственный анализ потока управления? Т.е. создать параллельную вселенную с закатом солнца вручную?
И тебя не напрягает, что ты не сможешь в коде использовать NRT и required?
Тогда зачем вообще нужен NRT?
А сложность такой системы ты точно правильно оцениваешь?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[27]: C# [Proposal] init block for safe initialization of
Здравствуйте, 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
Здравствуйте, _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.
Возьми мои примеры. Попробуй записать их как ты предлагаешь. Попробуй скомпилировать это текущим компилятором. Приведи их здесь если что-то получится.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.