Информация об изменениях

Сообщение Re[3]: Багофича C# - object initializer от 23.07.2024 16:30

Изменено 24.07.2024 8:05 Sinclair

Re[3]: Багофича C# - object initializer
Здравствуйте, Baiker, Вы писали:

B>По моему конкретному варианту в постскриптуме — вам нравится его реализация?

Нет, не нравится.

B>Если нет — давайте обсудим возражения.


B>Вот интересный пример со вложенностью, но я тут "множества нерешённых проблем" не вижу, дополняйте:


B>
B>int Width = 7;// обычная переменная в outer scope

B>var btn = new Button init B { // внимание! 'B' - это alias для создаваемого объекта (во внешнем scope это btn)
B>    .Width = Width + 50;// Button.Width = Width + 50 = 57
B>    .Invalidate();// такое MS не сделала даже за 20 лет!
B>    .Height = .Width + 1;// очевидно, что справа от = всё тот же контекст - взяли внутреннего мембера .Width 
B>    .Children.Add(niceIcon);
B>    .Styles = new HTMLStyle init { // как и просили, вложенный init
B>        .CSS = "color: #" + .Color;// Button.Color взят извне, т.к. HTMLStyle его не содержит
B>        .File = 1;// error! Нет такого мембера ни у HTMLStyle, ни у Button, внешний scope не рассматривается
B>        .Length = B.Length - 7;// ага! Вот где alias нужен. Вот теперь это HTMLStyle.Length = Button(B).Length - 7
B>    };
B>};
B>

Вот видите — первый же заданный вопрос заставил вас менять синтаксис. Теперь в init появился опциональный алиас. Причём теперь нужно не забыть его указать, иначе получится бяка.
Двигаемся дальше. Вот у вас в .Styles не было .Color, а вот он появился. Всё, поведение кода поменялось, без малейших подсказок со стороны компилятора. Удачной отладки.
Коммент про .File — а что, если бы у Button было такое свойство, то всё бы скомпилировалось?

Двигаемся дальше. В чём у нас семантика этого init? Слева от него — некое object expression, справа — опциональный алиас плюс некий блок кода.
Что это за блок кода такой? А это такой блок кода, где (это я угадываю телепатически)
1. Определён указанный выше алиас, если он есть
2. Разрешены только операторы присваивания и вызовы методов (причём не любых, см далее)
3. В аргументах методов и в выражениях операторов присваивания разрешены обращения к свойствам и методам через точку без object expression слева.

Для начала нужно убедиться, что у нас нет проблем с п.3. Не будет ли конфликтов при разборе? Я пока вижу один нехороший сценарий: взаимодействие с тернарным оператором и null-checking member access. Выражения типа x > y ? .Inc() похожи и на x > (y?.Inc()), и на (x > y)?.Inc()). А теперь у нас ещё и добавляется прекрасная возможность (x > y) ? (.Inc()), то есть "Syntax error, : expected".
Когда мы с этим разберёмся, захочется понять, какие именно методы можно вызывать, а какие — нет. Способ поиска методов слева от =, справа от =, и просто при записи .Invalidate() будет отличаться? Я так понимаю, что для "справа от = " вы хотите, чтобы выполнялся поиск вверх во всех init-блоках, пока не найдется подходящий метод. Будет ли это работать также без =? То есть если я пишу btn init{.Styles init {.Invalidate()}}; — будет ли успешно вызываться btn.Invalidate()?
Рассматриваются ли extension-методы?

Самое интересное тут вот в чём: как будем разруливать перегрузки методов? На всякий случай: в С# итак уже весьма навороченные (и местами неожиданные) правила байндинга вызовов (см.[url = /forum/dotnet/8761099.1]пример[/url]). Теперь вы предлагаете добавить к этой кунсткамере ещё одно измерение: подбор подходящего this в иерархии инициализируемых объектов. Как вы себе это видите — хотя бы в общих чертах? Вот у нас завелись два похожих метода: Button.Foo(int i), HtmlStyle.Foo(decimal d). Мы наблюдаем такое выражение: btn init{.Styles init {.Length = .Foo(42)}};. Какой из Foo будет вызван? Можно позвать .Styles.Foo — 42 implicitly convertible to decimal, и он доступен во внутреннем scope. Но, может быть, надо звать btn.Foo — ведь он better fit, т.к. там вообще не надо преобразовывать параметры? Или лучше всё же падать с CS0121: The call is ambiguous? Примерно к любому варианту, который вы выберете, я напишу контрпример, который работает "не так, как интуитивно ожидается".
Re[3]: Багофича C# - object initializer
Здравствуйте, Baiker, Вы писали:

B>По моему конкретному варианту в постскриптуме — вам нравится его реализация?

Нет, не нравится.

B>Если нет — давайте обсудим возражения.


B>Вот интересный пример со вложенностью, но я тут "множества нерешённых проблем" не вижу, дополняйте:


B>
B>int Width = 7;// обычная переменная в outer scope

B>var btn = new Button init B { // внимание! 'B' - это alias для создаваемого объекта (во внешнем scope это btn)
B>    .Width = Width + 50;// Button.Width = Width + 50 = 57
B>    .Invalidate();// такое MS не сделала даже за 20 лет!
B>    .Height = .Width + 1;// очевидно, что справа от = всё тот же контекст - взяли внутреннего мембера .Width 
B>    .Children.Add(niceIcon);
B>    .Styles = new HTMLStyle init { // как и просили, вложенный init
B>        .CSS = "color: #" + .Color;// Button.Color взят извне, т.к. HTMLStyle его не содержит
B>        .File = 1;// error! Нет такого мембера ни у HTMLStyle, ни у Button, внешний scope не рассматривается
B>        .Length = B.Length - 7;// ага! Вот где alias нужен. Вот теперь это HTMLStyle.Length = Button(B).Length - 7
B>    };
B>};
B>

Вот видите — первый же заданный вопрос заставил вас менять синтаксис. Теперь в init появился опциональный алиас. Причём теперь нужно не забыть его указать, иначе получится бяка.
Двигаемся дальше. Вот у вас в .Styles не было .Color, а вот он появился. Всё, поведение кода поменялось, без малейших подсказок со стороны компилятора. Удачной отладки.
Коммент про .File — а что, если бы у Button было такое свойство, то всё бы скомпилировалось?

Двигаемся дальше. В чём у нас семантика этого init? Слева от него — некое object expression, справа — опциональный алиас плюс некий блок кода.
Что это за блок кода такой? А это такой блок кода, где (это я угадываю телепатически)
1. Определён указанный выше алиас, если он есть
2. Разрешены только операторы присваивания и вызовы методов (причём не любых, см далее)
3. В аргументах методов и в выражениях операторов присваивания разрешены обращения к свойствам и методам через точку без object expression слева.

Для начала нужно убедиться, что у нас нет проблем с п.3. Не будет ли конфликтов при разборе? Я пока вижу один нехороший сценарий: взаимодействие с тернарным оператором и null-checking member access. Выражения типа x > y ? .Inc() похожи и на x > (y?.Inc()), и на (x > y)?.Inc()). А теперь у нас ещё и добавляется прекрасная возможность (x > y) ? (.Inc()), то есть "Syntax error, : expected".
Когда мы с этим разберёмся, захочется понять, какие именно методы можно вызывать, а какие — нет. Способ поиска методов слева от =, справа от =, и просто при записи .Invalidate() будет отличаться? Я так понимаю, что для "справа от = " вы хотите, чтобы выполнялся поиск вверх во всех init-блоках, пока не найдется подходящий метод. Будет ли это работать также без =? То есть если я пишу btn init{.Styles init {.Invalidate()}}; — будет ли успешно вызываться btn.Invalidate()?
Рассматриваются ли extension-методы?

Самое интересное тут вот в чём: как будем разруливать перегрузки методов? На всякий случай: в С# итак уже весьма навороченные (и местами неожиданные) правила байндинга вызовов (см. пример). Теперь вы предлагаете добавить к этой кунсткамере ещё одно измерение: подбор подходящего this в иерархии инициализируемых объектов. Как вы себе это видите — хотя бы в общих чертах? Вот у нас завелись два похожих метода: Button.Foo(int i), HtmlStyle.Foo(decimal d). Мы наблюдаем такое выражение: btn init{.Styles init {.Length = .Foo(42)}};. Какой из Foo будет вызван? Можно позвать .Styles.Foo — 42 implicitly convertible to decimal, и он доступен во внутреннем scope. Но, может быть, надо звать btn.Foo — ведь он better fit, т.к. там вообще не надо преобразовывать параметры? Или лучше всё же падать с CS0121: The call is ambiguous? Примерно к любому варианту, который вы выберете, я напишу контрпример, который работает "не так, как интуитивно ожидается".