Сложное (многоклассовое) наследование и полиморфизм - возможно или нет?
От: SergeyY  
Дата: 26.10.17 21:11
Оценка:
Сложное (многоклассовое) наследование и полиморфизм — возможно или нет?

Когда я сталкивался ранее с наследованием классов и полиморфизмом, то это было или,

1) дописывание каких то новых методов в класс-потомок, которых не было в классе-предка. (обычное наследование).
2) переопределение некоторых методов, объявленных в базовом классе как virtual, (обычный полиморфизм), причем сигнатурна методов оставалась одинаковой.

А можно ли унаследовать сразу множество связанных классов? Т.е. как бы целый проект из связных классов, унаследуется от базового проекта, тех же классов,
каждый в своем множестве унаследован от класса-прототипа в базовом проекте.
Чтобы было понятнее, что имеется в виду, 1) упростим задачу до предела — множество из 2-х классов, должно унаследоваться от другого множества из 2-х классов.
(на практике, если есть такая возможность с двумя, то можно хоть 10, хоть 100, хоть любое количество классов в множестве унаследовать).
2) напишу на псевдо-C# языке, чтобы было понятнее, что имею в виду.


Допустим, ранее был написан некий проект с названием Module, который для простоты, состоит из 2-х классов.
Позже, нам нужно эти два класса унаследовать в проекте Special, который как бы, расширяет возможности Module-проекта.

//------------------------------

// Получается, в простейшем варианте, проект Module, допустим, состоял из двух работоспособных классов.


public class ModuleEntity // некая сущность в базовом проекте Module
{
public int ab;
public int cd; // некие поля описывающие сущность
}



public class ModuleAlgorithmic // некая сущность в базовом проекте Module
{
public ModuleEntity entity;

public virtual ModuleEntity[] AlgorithmOfModule(ModuleEntity param)
{
// ...
}
}



//------------------------------

// Как нужно унаследовать сразу два класса. В новом проекте Special.


public class SpecialEntity : ModuleEntity // некая сущность в базовом проекте Module. Наследование этого класса понятно.
{
public int ef; // ну и понятно, будут доступны ab, cd от ModuleEntity
}

//------------------------------

А вот дальше, нам нужно унаследовать класс SpecialAlgorithmic от ModuleAlgorithmic, да так, чтобы 1) его единственное поле ModuleEntity entity;
просто стало полем унаследуемого типа, SpecialEntity entity; 2) Метод AlgorithmOfModule — нужно переопределить (override), да так, что
его сигнатура "почти такая же", но не совсем — этот метод должен уже принимать унаследуемые ссылки на SpecialEntity вместо ModuleEntity.
Т.е. как написать что то типа такого (привожу на псевдо-языке, но должно быть понятно, что имеется в виду).



public class SpecialAlgorithmic : ModuleAlgorithmic // некая сущность в базовом проекте Module
{
public SpecialEntity entity : override ModuleEntity entity; // override field from ModuleAlgorithmic class — field entity

public override SpecialEntity[] AlgorithmOfModule(SpecialEntity param) : ModuleEntity[] AlgorithmOfModule(ModuleEntity param)
{
// ...
}
}


//------------------------------


Что же в таком случае получается? Допустим у нас первый проект, с классами ModuleEntity, ModuleAlgorithmic — полностью работоспособный,
и выводит некий результат после


ModuleEntity param = new ModuleEntity( /* ... */ );
ModuleEntity modEnt = new ModuleEntity( /* параметры создающие объект */ );
ModuleAlgorithmic modAlg = new ModuleAlgorithmic(modEnt); // приняв modEnt, запишет в поле entity
ModuleEntity[] result = modAlg.AlgorithmOfModule(param) // где param — тоже ModuleEntity


// нам нужно получить точно такой же результат, если заменим в этом блоке из 4 строк, все "Module" на "Special"


SpecialEntity param = new SpecialEntity( /* ... */ );
SpecialEntity modEnt = new SpecialEntity( /* параметры создающие объект */ ); // здесь проблем нет — унаследовано и добавлено поле int ef;
SpecialAlgorithmic modAlg = new SpecialAlgorithmic(modEnt); // приняв modEnt, запишет в поле entity !! но уже в SpecialEntity entity : который override (не ModuleEntity типа)
SpecialEntity[] result = modAlg.AlgorithmOfModule(param) // где param — тоже ModuleEntity , но точнее — унаследуемый тип SpecialEntity от ModuleEntity


//------------------------------


Казалось бы, всё просто — унаследовали SpecialEntity от ModuleEntity, добавив ему поле (здесь проблем нет).
Второе — унаследовали SpecialAlgorithmic от ModuleAlgorithmic, чтобы в конечном итоге можно было вызвать точно такой же код, только заменив слова 'Module' на 'Special'.
Виртуальный метод AlgorithmOfModule будет тоже как надо, переопределен.

НО!! Вот загвоздка — 1) нужно то еще первое — переопределить его сигнатуру, т.е. чтобы метод принимал и возвращал объекты другого типа, пусть и унаследуемого!
2) поле лишнее entity — тоже создавать НЕ ВАРИАНТ — больше памяти будет занимать (допустим алгоритм имеет сильно упирается в память), а значит нужно переопределить
внутреннее ПОЛЕ.

Если кто понял, то подскажите, как средствами C# и .NET (а может и C++) — можно решить такую задачу "сложного многоклассового наследования" ?
Чтобы ни лишние поля в классе-обертке не создавать, и метод переопределить с унаследованной сигнатурой. Если это возможно, то целые проекты со многими
связными классами аналогично можно унаследовать, переопределив только нужную часть, а в вызывающем коде, нужно будет только заменить название-хидер проекта,
как у нас — заменяли 'Module' на 'Special'.
Какие нибудь "шаблоны проектирования" подобное позволяют?

Заранее, спасибо.
Re: Сложное (многоклассовое) наследование и полиморфизм - возможно или нет?
От: Ромашка Украина  
Дата: 26.10.17 21:17
Оценка:
Здравствуйте, SergeyY, Вы писали:
SY>НО!! Вот загвоздка — 1) нужно то еще первое — переопределить его сигнатуру, т.е. чтобы метод принимал и возвращал объекты другого типа, пусть и унаследуемого!
SY>2) поле лишнее entity — тоже создавать НЕ ВАРИАНТ — больше памяти будет занимать (допустим алгоритм имеет сильно упирается в память), а значит нужно переопределить внутреннее ПОЛЕ.

Ты про генерики?


Всё, что нас не убивает, ещё горько об этом пожалеет.
Re[2]: Сложное (многоклассовое) наследование и полиморфизм - возможно или нет?
От: SergeyY  
Дата: 26.10.17 21:23
Оценка:
Здравствуйте, Ромашка, Вы писали:

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

SY>>НО!! Вот загвоздка — 1) нужно то еще первое — переопределить его сигнатуру, т.е. чтобы метод принимал и возвращал объекты другого типа, пусть и унаследуемого!
SY>>2) поле лишнее entity — тоже создавать НЕ ВАРИАНТ — больше памяти будет занимать (допустим алгоритм имеет сильно упирается в память), а значит нужно переопределить внутреннее ПОЛЕ.

Р>Ты про генерики?


У нас есть полностью работоспособный проект с названием "Module", и классами ModuleEntity , ModuleAlgorithmic,
никаких генериков в нем нет.
Как унаследовать сразу целый проект, в данном случае — в классе SpecialAlgorithmic — наследуется поле, но с переопределением типа,
и переопределяется метод (override) чтобы и сигнатура была из производных типов.

Короче в вызывающем коде, чтобы было всё то же, кроме замены слова 'Module' на 'Special'.
Re[3]: Сложное (многоклассовое) наследование и полиморфизм - возможно или нет?
От: Ромашка Украина  
Дата: 26.10.17 21:48
Оценка:
Здравствуйте, SergeyY, Вы писали:
SY>Как унаследовать сразу целый проект, в данном случае — в классе SpecialAlgorithmic — наследуется поле, но с переопределением типа,
SY>и переопределяется метод (override) чтобы и сигнатура была из производных типов.

Так нельзя. Нельзя переопределить поле с новым типом или метод с другой сигнатурой.

PS Если есть доступ к исходникам первого проекта — меняйте типы на генерики.


Всё, что нас не убивает, ещё горько об этом пожалеет.
Re[4]: Сложное (многоклассовое) наследование и полиморфизм -
От: SergeyY  
Дата: 27.10.17 04:56
Оценка:
Здравствуйте, Ромашка, Вы писали:

Р>Так нельзя. Нельзя переопределить поле с новым типом или метод с другой сигнатурой.



Фактически, насчет метода, мы можем его переопределить с той же сигнатурой, т.к. тип наследуется

псевдокод класса SpecialAlgorithmic —

public override SpecialEntity[] AlgorithmOfModule(SpecialEntity param) : ModuleEntity[] AlgorithmOfModule(ModuleEntity param)
{
// ...
}
}


будет примерно таким

public override ModuleEntity[] AlgorithmOfModule(ModuleEntity param)
{
SpecialEntity param2 = (SpecialEntity) param; // переданный объект преобразовали к производному типу явно.
// ...
return (ModuleEntity[]) result; // а то что возвращаем — преобразовали обратно к базовому
}
}


Фактически то же самое можно было бы сделать на уровне компилятора ООП-языка, чтобы в вызывающем коде, могли просто
пользоваться методом с сигнатурой типов, которые унаследованы, и не писать явные преобразования типов (SpecialEntity) param.
Т.е. как написано выше, просто правильно объявить метод.

--------------------


public override SpecialEntity[] AlgorithmOfModule(SpecialEntity param) : ModuleEntity[] AlgorithmOfModule(ModuleEntity param)
{
// ...
}
}

и в вызывающем коде просто вызвать его, как если бы он принимал SpecialEntity а не MoluleEntity —

SpecialEntity param = new SpecialEntity( /* ... */ );
SpecialEntity modEnt = new SpecialEntity( /* параметры создающие объект */ );
SpecialAlgorithmic modAlg = new SpecialAlgorithmic(modEnt);
SpecialEntity[] result = modAlg.AlgorithmOfModule(param) ;


--------------------


Т.е. я предложил вариант, который, если бы был реализован на уровне компилятора ООП-языка, он бы расширил возможности ООП.
Целый проект можно было бы унаследовать от другого, и в вызывающем коде просто изменить хидер объектов всех создаваемых классов.

Второе — переопределение поля в наследуемом классе.
Вот в чем разница? Первое — мы унаследовали один класс от другого,

public class ModuleEntity // некая сущность в базовом проекте Module
{
public int ab;
public int cd; // некие поля описывающие сущность
}



public class SpecialEntity : ModuleEntity // некая сущность в базовом проекте Module. Наследование этого класса понятно.
{
public int ef; // ну и понятно, будут доступны ab, cd от ModuleEntity
}


Т.е. поля ab, cd остались и появилось что то новое.
В чем принципиальная разница с тем если объект этого класса будет полем в другом классе,


public class ModuleAlgorithmic // некая сущность в базовом проекте Module
{
public ModuleEntity entity;
}



public class SpecialAlgorithmic : ModuleAlgorithmic // некая сущность в базовом проекте Module
{
public SpecialEntity entity : override ModuleEntity entity; // override field from ModuleAlgorithmic class — field entity
}



Фактически, хотелось бы того же самого, т.е. чтобы поле entity внутри класса SpecialAlgorithmic — просто расширилось, и не надо было создавать новое.
(точно так же расширились данные в производном классе SpecialEntity — ab и cd — остаются в нём)

Если это невозможно сделать, то приходится в классе SpecialAlgorithmic создавать другое поле, с ab cd ef, а "лишнее" entity с ab, cd
уже использоваться не будет и будет просто занимать лишнюю память, что в алгоритме, чувствительным к памяти, будет существенным недостатком.


Потому, и интересно, может быть, есть какие то возможности в языке C# или C++ реализовать подобное сложное "многоклассовое" наследование + полиморфизм?
Есть же всякие шаблоны проектирования, может существует и такой, с приближенными возможностями?

Хотелось бы просто, реализовав эти два проекта, иметь возможность в вызывающем коде просто заменить слово 'Module' на название проекта,
(в приведенном случае это было 'Special') и получить работоспособную программу.
Отредактировано 27.10.2017 5:09 SergeyY . Предыдущая версия . Еще …
Отредактировано 27.10.2017 5:03 SergeyY . Предыдущая версия .
Отредактировано 27.10.2017 5:01 SergeyY . Предыдущая версия .
Отредактировано 27.10.2017 5:00 SergeyY . Предыдущая версия .
Re[5]: Сложное (многоклассовое) наследование и полиморфизм -
От: SergeyY  
Дата: 27.10.17 05:08
Оценка:
меняйте типы на генерики.


Если это единственный вариант, то как использовать к примеру? Генерики же вроде, позволяют просто подставить любой тип,
а то что один наследуется от другого — это преимущество в них не реализуется?
Отредактировано 27.10.2017 5:25 SergeyY . Предыдущая версия . Еще …
Отредактировано 27.10.2017 5:09 SergeyY . Предыдущая версия .
Re: Сложное (многоклассовое) наследование и полиморфизм - возможно или нет?
От: velkin Удмуртия https://kisa.biz
Дата: 27.10.17 12:05
Оценка:
Здравствуйте, SergeyY, Вы писали:

SY>Сложное (многоклассовое) наследование и полиморфизм — возможно или нет?


Не знаком с таким понятием как "многоклассовое наследование". Мне лень разбираться в смысле написанного, но если речь о множественном наследовании, то в дотнете его не было с самого начала появления. Вот подгон из гугла почему его нет:
  Почему в C# отказались от множественного наследования классов?
Почему в C# отказались от множественного наследования классов?


Есть несколько причин почему мы не реализовали множественное наследование реализации напрямую. (Как вы знаете, мы поддерживаем множественное наследование интерфейсов).

Однако, я должен обратить внимание на то, что компиляторы могут создавать множественное наследование для их типов внутри CLR. Есть несколько проблем, если идти этим путем: результат непроверяем, отсутствие взаимодействия с другими языками через CLS. Техника для создания некоторых VTables в RVA-шных статических полях. Для того, чтобы адреса управляемых методов (которые, возможно, даже не были обработаны JIT), вы используете конструкцию VTFixup. Эта конструкция представляет собой таблицу триплетов. Триплеты состоят из маркера на управляемый метод, адреса в вашем образе, который должен будет зафиксироваться (в этом случае, слот в VTable вы создаете в RVA-шных статических полях), и некоторых флагов. Возможные флаги описаны в corhdr.h. Они позволяют указать размер указателей: 32 бита vs 64 бита, контролировать виртуальное поведение, а также есть ли какой-нибудь режим обратного PInvoke, который следует применять в виде отложенных вычислений, что в итоге передаются в управляемый метод. Если мы выполняем переход unmanaged->managed, мы так же можем контролировать какой AppDomain будет выбран, чтобы отправить вызов.

Есть несколько причин, почему мы не предоставили продуманную, поддающуюся проверке, CLS-совместимую версию множественного наследования реализации:

Разные языки имеют разные ожидания того, как работает множественное наследование. Например, как решаются конфликты и будут ли дубликаты объединены или отброшены. Прежде чем мы сможем реализовать множественное наследование в CLR, мы должны сделать обзор всех языков, определить общие понятия, и решить как выразить их нейтральным способом. Мы должны решить будет ли множественное наследование входить в CLS и что это будет означать для языков, которые не хотят поддерживать эту концепцию (например VB.NET). Конечно это наше дело в CLR, но мы пока не нашли время сделать это для множественного наследования.

Мест, где хорошо подходит множественное наследование, на самом деле очень мало. Во многих случаях работает множественное наследование интерфейсов. В других случаях можно использовать инкапсуляцию и делегирование. Если бы мы добавили другую конструкцию, например mixins, что было бы более мощным?

Множественное наследование реализации добавляет сложности для реализации. Эта сложность влияет на кастинг, сериализацию, доступ к полям рефлексию и возможно многие другие места.

Все еще не совсем ясно, что эта возможность окупит себя. Это то о чем мы часто спрашиваем себя. Это нечто, что мы еще не до конца проверили. Но мое нутро говорит, что после глубокого анализа мы решим не добавлять эту особенность.

Если взять C++ ISO/IEC 14882, который микрософты любят называть native C++ подчёркивая, что это не их мелкомягкая поделка на дотнете, то в нём реализованы четыре парадигмы программирования: процедурная, функциональная, объектно-ориентированная и обобщённая (шаблоны C++, generic). Я не рассматриваю любые другие конструкции натянутые поверх языка. Множественное наследование относится к объектно-ориентированному программированию, то есть не к обобщённому, если кто захочет поговорить об этом. Применительно к полиморфизму различия выглядят так:



Теперь, когда понятно, что от множественного наследования отказались, если конечно речь о нём, то смотрим Наследование (программирование).

Множественное наследование — потенциальный источник ошибок, которые могут возникнуть из-за наличия одинаковых имён методов в предках. В языках, которые позиционируются как наследники «C++» («Java», «C#» и другие), от множественного наследования было решено отказаться в пользу интерфейсов. Практически всегда можно обойтись без использования данного механизма. Однако, если такая необходимость всё-таки возникла, то для разрешения конфликтов использования наследованных методов с одинаковыми именами возможно, например, применить операцию расширения видимости — «::» — для вызова конкретного метода конкретного родителя.

Попытка решения проблемы наличия одинаковых имён методов в предках была предпринята в языке «Eiffel», в котором при описании нового класса необходимо явно указывать импортируемые члены каждого из наследуемых классов и их именование в дочернем классе.

Большинство современных объектно-ориентированных языков программирования («C#», «Java», «Delphi» и другие) поддерживают возможность одновременно наследоваться от класса-предка и реализовать методы нескольких интерфейсов одним и тем же классом. Этот механизм позволяет во многом заменить множественное наследование — методы интерфейсов необходимо переопределять явно, что исключает ошибки при наследовании функциональности одинаковых методов различных классов-предков.

Причём под интерфейсом подразумевают не открытую часть класса, а именно шаблон проектирования. К тому же в C# в отличие от того же C++ для этого имеется ключевое слово. Опять же архитектуры приложений и шаблоны проектирования не являются каким-то секретом, всё уже давно известно и гуглится при правильном запросе. При этом можно пойти от противного, создать архитектуру хотя бы в виде UML диаграмм классов и потом посмотреть какому шаблону проектирования это соответствует для так сказать последующей корректировки кода к наилучшему варианту, но не для самого проектирования.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.