Есть два основных интерфейса и несколько ветвящихся от базового классов.
Логика работы строится исходя из того, какие у класса есть интерфейсы или их вообще нет.
Из-за того что один и тот же интерфейс нужно поддерживать в дочерних классах разных веток, приходится его методы держать в базовом классе (чтобы лишний раз не копипастить).
За сим две проблемы
1) из промежуточных классов торчат методы, которых у них быть не должно (как минимум раздражает на этапе написания кода).
2) народ может пытаться с ними работать, хотя должен работать только с классами у которых чётко прослеживается интерфейс.
В плюсах это решилось бы просто, из базового класса всё выкинулось бы и определилось бы в другом классе от которого бы дополнительно наследовались только нужные классы.
А в C# как с этим бороться? Можно ли как-то у дочернего класса задавить то что определено в базовом, а потом снова открыть в нужном потомке?
Здравствуйте, QuAzI, Вы писали:
QAI>Есть два основных интерфейса и несколько ветвящихся от базового классов. QAI>Логика работы строится исходя из того, какие у класса есть интерфейсы или их вообще нет. QAI>Из-за того что один и тот же интерфейс нужно поддерживать в дочерних классах разных веток, приходится его методы держать в базовом классе (чтобы лишний раз не копипастить). QAI>За сим две проблемы
QAI>1) из промежуточных классов торчат методы, которых у них быть не должно (как минимум раздражает на этапе написания кода).
Выделите эти методы в отдельный класс реализующий интерфейс, используйте этот класс в подклассах при реализации этого интерфейса.
QAI>2) народ может пытаться с ними работать, хотя должен работать только с классами у которых чётко прослеживается интерфейс. QAI>В плюсах это решилось бы просто, из базового класса всё выкинулось бы и определилось бы в другом классе от которого бы дополнительно наследовались только нужные классы. QAI>А в C# как с этим бороться? Можно ли как-то у дочернего класса задавить то что определено в базовом, а потом снова открыть в нужном потомке?
Задавить можно, но только другим методом, так что смысла нет.
QAI>Примерная диаграмма ветвления классов QAI>Image: zo9OyZN.png
Здравствуйте, QuAzI, Вы писали:
QAI>А в C# как с этим бороться? Можно ли как-то у дочернего класса задавить то что определено в базовом, а потом снова открыть в нужном потомке?
Три стандартных способа:
1. Логика в protected-методах, наследники выставляют в public.
2. Переиспользуемая логика в отдельном классе, инстанс класса хранится в поле, методы интерфейса вызывают код из класса.
3. Любой il weawer/emitter, позволяющий автоматизировать п.2. Fody/Automapper, если навскидку.
На практике самый простой способ — пересмотреть подход к проектированию. Если не секрет — поделитесь реальной задачей, на которой без множественного наследования прям вообще никак. Интересно ж
P.S. Размышления на тему от Липперта, в пяти частях.
Здравствуйте, Sinix, Вы писали:
S>На практике самый простой способ — пересмотреть подход к проектированию. Если не секрет — поделитесь реальной задачей, на которой без множественного наследования прям вообще никак. Интересно ж
Здравствуйте, hardcase, Вы писали:
S>>Если не секрет — поделитесь реальной задачей, на которой без множественного наследования прям вообще никак. H>Например AST для пространств имен и классов.
Ага, спасиб! Отдельное спасибо за пример, ещё один способ решения вспомнил, аля MS.Cci/Roslyn:
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, hardcase, Вы писали:
S>>>Если не секрет — поделитесь реальной задачей, на которой без множественного наследования прям вообще никак. H>>Например AST для пространств имен и классов.
S>Ага, спасиб! Отдельное спасибо за пример, ещё один способ решения вспомнил, аля MS.Cci/Roslyn: S>
S> // GenericNestedTypeInstanceReference.cs
S> public override Cci.INestedTypeReference AsNestedTypeReference
S> {
S> get { return this; }
S> }
S> // GenericNamespaceTypeInstanceReference.cs
S> public override Microsoft.Cci.INestedTypeReference AsNestedTypeReference
S> {
S> get { return null; }
S> }
S>
S>В чём прелесть: этот вариант и контракты документирует, и со всеми перечисленными выше способами (в т.ч. и твоим) совместим.
Спасибо, интересные варианты.
В принципе почти сразу после поста я сделал всё через protected в базовом классе и раскрыл как public в классах поддерживающих интерфейсы. Правда чтобы не переопределять через new исользовал троху кривое именование, m_ префикс для свойств и суффикс _internal для методов. Не уверен что красивое решение, но чтобы не путаться как-то так решил их обозначить.
По последнему примеру что-то наглядное кроме сорсов собственно рослина не гуглится, а я как-то туплю и не понимаю как этот подход поюзать в .NET2/.NET4
Про дизайн — возможно вы правы, но уж как есть, переписывать с нуля слишком дорого и мне пока не хватает опыта/мозгов, чтобы задизайнить мою задачу лучше.
Здравствуйте, QuAzI, Вы писали:
QAI>Правда чтобы не переопределять через new исользовал троху кривое именование, m_ префикс для свойств и суффикс _internal для методов. Не уверен что красивое решение, но чтобы не путаться как-то так решил их обозначить.
Обычно добавляют суффиксы Core, Internal или Impl, но это уже из разряда про вкус фломастеров.
QAI>По последнему примеру что-то наглядное кроме сорсов собственно рослина не гуглится, а я как-то туплю и не понимаю как этот подход поюзать в .NET2/.NET4
Да так же всё. Просто в дополнение к наследованию те же интерфейсы через свойства выставляются. Смысл в том, что не нужно помнить все интерфейсы, они в свойствах базового типа задокументированы.
QAI>Про дизайн — возможно вы правы, но уж как есть, переписывать с нуля слишком дорого и мне пока не хватает опыта/мозгов, чтобы задизайнить мою задачу лучше.
А, если код уже написан, то разумеется не надо его бросаться заменять без явной необходимости. Я понял вопрос как "как спроектировать", потому и посоветовал
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, QuAzI, Вы писали:
QAI>>А в C# как с этим бороться? Можно ли как-то у дочернего класса задавить то что определено в базовом, а потом снова открыть в нужном потомке?
S>Три стандартных способа: S>1. Логика в protected-методах, наследники выставляют в public. S>2. Переиспользуемая логика в отдельном классе, инстанс класса хранится в поле, методы интерфейса вызывают код из класса.
Для интерфейсов также можно писать extension методы