Здравствуйте, Codealot, Вы писали:
C>есть внятные объяснения?
Явное наследование через base() есть.
Если сделать неявное, получается борода:
1. Возьмем 3 сборки:
* ParentAssembly с ParentClass(int x)
* ChildAssembly с ChildClass(int x) : base(x), сгенерированный компилятором
* UserAssembly, создающую ChildClass.
2. Меняем в ParentAssembly int x -> string x.
3. Пересобираем ParentAssembly и ChildAssembly. Все собралось без ошибок и зарелизилось.
4. Где-то в другой временной зоне громко ругается разработчик UserAssembly, который начал вываливаться с MethodNotFoundException.
Явное определение конструкторов (которое на раз генерируется IDE) это предотвращает — ChildAssembly выдаст ошибку на этапе компиляции, разработчик вспомнит, что это public API и откатит все взад.
Если очень надо и часто меняются параметры — сделай wrapper class, как с EventArgs.
Здравствуйте, Quebecois, Вы писали:
Q>1. Возьмем 3 сборки: Q>
Q>* ParentAssembly с ParentClass(int x) Q>* ChildAssembly с ChildClass(int x) : base(x), сгенерированный компилятором Q>* UserAssembly, создающую ChildClass. Q>Q>2. Меняем в ParentAssembly int x -> string x. Q>3. Пересобираем ParentAssembly и ChildAssembly. Все собралось без ошибок и зарелизилось. Q>4. Где-то в другой временной зоне громко ругается разработчик UserAssembly, который начал вываливаться с MethodNotFoundException.
Q>Явное определение конструкторов (которое на раз генерируется IDE) это предотвращает — ChildAssembly выдаст ошибку на этапе компиляции, разработчик вспомнит, что это public API и откатит все взад.
Ну дык ctor ParentClass тоже public api, но разработчик спокойно его поменял.
Re: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Codealot, Вы писали:
C>есть внятные объяснения?
Quebecois прекрасно всё объяснил, но скорее с практической точки зрения. Public API не должен меняться вообще, если так по-хорошему.
В теории, смысл такой:
{{ базовый класс
констр 1
констр 2
}}
{{ потомок
констр 3
}}
Когда ты создаёшь класс-потомок, ты наследуешь поведение базы. НО(!) никто не сказал, что базовые конструкторы вообще подходят под конструирование потомка! Если ты ввёл "единственно правильный" констр 3, то юзер класса "потомок" вообще не должен никогда видеть констр 1! Что, собственно, язык и делает. Хочешь "пробросить" конструктор — не вопрос, просто создай его руками.
Что в C# налажали, так это вызов самих конструкторов. Если я правильно ошибаюсь, в Delphi можно вызывать базовый конструктор из любого места текущего конструктора. В C# это возможно только в начале. А может это я с D перепутал.
Здравствуйте, Baiker, Вы писали:
B>Что в C# налажали, так это вызов самих конструкторов. Если я правильно ошибаюсь, в Delphi можно вызывать базовый конструктор из любого места текущего конструктора. В C# это возможно только в начале. А может это я с D перепутал.
В жаве та же фигня. Капец бесит.
Так можно
MyCtor() {
super(something());
}
А так нельзя
MyCtor() {
int s = something();
super(s);
}
Лавров.jpg
Re[2]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Baiker, Вы писали:
B>Что в C# налажали, так это вызов самих конструкторов. Если я правильно ошибаюсь, в Delphi можно вызывать базовый конструктор из любого места текущего конструктора. В C# это возможно только в начале.
И зачем эта фича нужна?
Какие-то минимальные расчёты можно и в текущем варианте передать в вызове base.
Если там какая-то сложная логика, то вообще странно в конструктор это пихать.
Тут уже какая-нибудь фабрика нужна, которая и асинхронность сможет предоставить.
Re[2]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Quebecois, Вы писали:
Q>3. Пересобираем ParentAssembly и ChildAssembly. Все собралось без ошибок и зарелизилось. Q>4. Где-то в другой временной зоне громко ругается разработчик UserAssembly, который начал вываливаться с MethodNotFoundException.
А если ты сигнатуру метода в ParentAssembly.ParentClass поменяешь, который вызывается из UserAssembly?
Ад пуст, все бесы здесь.
Re[5]: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, νsb, Вы писали:
S>>Ога, класс, особенно если something виртуальный метод. νsb>При чём тут это. Виртуальные методы нельзя вызывать, пока не вызвал конструктор базового объекта.
Если компилятор такое поймать сможет, то да, проблем нет, иначе -- хреново.
Кодом людям нужно помогать!
Re[6]: почему в C# до сих пор нет наследования конструкторов
Здравствуйте, Sharov, Вы писали:
S>>>Ога, класс, особенно если something виртуальный метод. νsb>>При чём тут это. Виртуальные методы нельзя вызывать, пока не вызвал конструктор базового объекта.
S>Если компилятор такое поймать сможет, то да, проблем нет, иначе -- хреново.
Сможет, конечно. Ну в жаве по крайней мере может.
Проблема возникает, когда нужно что-то не совсем тривиальное. К примеру в конструктор базового класса нужно передавать два параметра. И чтобы эти параметры вычислить, нужна еще пара строк, которые никакие виртуальные методы вызывать не будут, просто какие-то простые преобразования от параметров конструктора. Сейчас это в жаве никак не сделаешь, ну только вычислять это два раза в статическом методе, что смотрится очень некрасиво. Или заменить конструктор на статический метод, который будет конструировать объект, что тоже кажется некрасивым усложнением на ровном месте.
Условно:
Могло бы быть:
MyCtor(double d) {
double a = atan(d);
double s = sin(a);
double c = cos(a);
super(s, c);
}
Здравствуйте, Codealot, Вы писали:
C>Здравствуйте, Quebecois, Вы писали:
Q>>3. Пересобираем ParentAssembly и ChildAssembly. Все собралось без ошибок и зарелизилось. Q>>4. Где-то в другой временной зоне громко ругается разработчик UserAssembly, который начал вываливаться с MethodNotFoundException.
C>А если ты сигнатуру метода в ParentAssembly.ParentClass поменяешь, который вызывается из UserAssembly?
Методы на уровне метаданных референсятся из ParentAssembly. Конструкторы, физически находящиеся в ChildAssembly — из нее. Поломка обратной совместимости между ChildAssembly и UserAssembly при модификации BaseAssembly и без модификации ChildAssembly — архитектурный косяк. Еще получается, что из одних и тех же исходников ChildAssembly будет собираться 2 несовместимые друг с другом версии сборки, в зависимости от того, что было в BaseAssembly.
Вообще, золотое правило дизайна сложных систем — каждое изменение отдельного параметра должно иметь минимальные по сложности (и предсказуемости) последствия. Иначе начинается каскад непредвиденных проблем при каждом чихе. В текущей реализации изменение сигнатуры конструктора повлияет только на конкретные методы, конкретно его вызывающие. Они вылетят с ошибками компиляции и необходимость их правки защитит от непреднамеренной поломки на порядок большего количества кода.
Ну и дисклеймер: такие решения 100% субъективны — это judgment call архитектора в плане удобство vs. вероятность глюков. Точек зрения об идеальном балансе тут может быть больше, чем разработчиков, и спорить о них — дело неблагодарное. Обычно, чья зона ответственности — тот и решает (и несет ответственность). Я лишь попытался объяснить точку зрения человека, принявшего решение не наследовать автоматически конструкторы.
Здравствуйте, karbofos42, Вы писали:
K>Здравствуйте, Quebecois, Вы писали:
Q>>Явное наследование через base() есть.
K>Если много конструкторов, то замучаешься их вот так дублировать в каждом наследнике и потом поддерживать.
Я же написал ниже: делаешь BaseClass(ConstructionRequest rq) и передаешь его. Runtime overhead будет минимальный, поддерживать ничего не надо. Вся система EventHandler-ов на этом построена.
K>В С++ есть вот такая штука: K>https://learn.microsoft.com/en-us/cpp/cpp/constructors-cpp?view=msvc-170#inheriting_constructors K>
K>class Derived : Base
K>{
K>public:
K> // Inherit all constructors from Base
K> using Base::Base;
K>...
K>
В плюсах семантика другая — чтобы скомпилировать Derived из исходников, надо рекурсивно распарсить Base и еще миллион заголовков. C# специально разработали так, чтобы вместо этого хватало метаданных сборок из references с хеш-индексами. В итоге, компиляция быстрее на несколько порядков.
Re: почему в C# до сих пор нет наследования конструкторов?
Здравствуйте, Codealot, Вы писали:
C>есть внятные объяснения?
А можно поподробнее объяснить, что имеется в виду?
Какой сценарий использования предполагается?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Q>* ParentAssembly с ParentClass(int x) Q>* ChildAssembly с ChildClass(int x) : base(x), сгенерированный компилятором Q>* UserAssembly, создающую ChildClass. Q>Q>2. Меняем в ParentAssembly int x -> string x. Q>3. Пересобираем ParentAssembly и ChildAssembly. Все собралось без ошибок и зарелизилось. Q>4. Где-то в другой временной зоне громко ругается разработчик UserAssembly, который начал вываливаться с MethodNotFoundException.
Простите, но вы неправы. Описанный вами сценарий и не должен работать, и не работает в С# независимо от наличия автоматически унаследованных конструкторов.
Корень проблемы, которую вы описываете в своём примере, не в том, что там что-то автоматически отнаследовалось, а в том, что владелец UserAssembly пытается использовать её без перекомпиляции после того, как одна из зависимостей (ChildAssembly) изменилась.
Дотнет не рассчитан на такое использование. Правило большого пальца: если изменилась хотя бы одна из зависимостей, проект нужно пересобирать.
Исключения из этого правила очень редки, и требуют специальных усилий. Ну, там, к примеру, если вы пишете приложение, которое должно уметь работать с плагинами — в некотором смысле, плагины являются для вас зависимостями; но там вы будете прилагать специальные меры как со стороны приложения, так и со стороны плагинов (в виде набора ограничений на типы, экспортируемые из плагина) для предотвращения проблем с версионированием метаданных.
Авторы .Net, конечно же, предпринимают усилия при публикации апдейтов — так, чтобы приложения, собранные под .Net 6.0.0, продолжали работать под .Net 6.0.3 без перекомпиляции.
И эти усилия, в частности, включают в себя запрет на внезапные модификации сигнатур конструкторов.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Quebecois, Вы писали:
Q>Я же написал ниже: делаешь BaseClass(ConstructionRequest rq) и передаешь его. Runtime overhead будет минимальный, поддерживать ничего не надо. Вся система EventHandler-ов на этом построена.
ну, допустим, у меня есть базовый класс коллекции со следующими конструкторами:
public CollectionBase();
public CollectionBase(int capacity);
public CollectionBase(params T[] items);
public CollectionBase(IEnumerable<T> items);
public CollectionBase(IReadOnlyCollection<T> items);
как мне сделать наследника, у которого будут все эти конструкторы продублированы? Или как будет выглядеть аналог с ConstructionRequest?
Q>В плюсах семантика другая — чтобы скомпилировать Derived из исходников, надо рекурсивно распарсить Base и еще миллион заголовков. C# специально разработали так, чтобы вместо этого хватало метаданных сборок из references с хеш-индексами. В итоге, компиляция быстрее на несколько порядков.
Методы же как-то наследуются, а конструкторы чем-то принципиально от методов отличаются?
Здравствуйте, Sinclair, Вы писали: S>Простите, но вы неправы. Описанный вами сценарий и не должен работать, и не работает в С# независимо от наличия автоматически унаследованных конструкторов.
Работает.
пруф
Минимальное репро — распакуется через patch -p1 < RefTest.patch:
UserAssembly создает экземпляр ChildClass без прямого reference на ParentAssembly. Т.е. на уровне метаданных достаточно вещей, вытянутых из ChildAssembly. S>Корень проблемы, которую вы описываете в своём примере, не в том, что там что-то автоматически отнаследовалось, а в том, что владелец UserAssembly пытается использовать её без перекомпиляции после того, как одна из зависимостей (ChildAssembly) изменилась.
Я не говорю, что это хорошая user practice. Это просто пример того, что сейчас работает, и сломалось бы при добавлении наследования конструкторов. S>Дотнет не рассчитан на такое использование. Правило большого пальца: если изменилась хотя бы одна из зависимостей, проект нужно пересобирать. S>Исключения из этого правила очень редки, и требуют специальных усилий. Ну, там, к примеру, если вы пишете приложение, которое должно уметь работать с плагинами — в некотором смысле, плагины являются для вас зависимостями; но там вы будете прилагать специальные меры как со стороны приложения, так и со стороны плагинов (в виде набора ограничений на типы, экспортируемые из плагина) для предотвращения проблем с версионированием метаданных.
Я про плагины и говорил. Сейчас достаточно публичные интерфейсы вынести в отдельную assembly и не трогать существующие типы в ней. С наследованием это перестанет работать.