Так как множественное наследование является плохим (это усложняет исходный код), C # не предоставляет такой шаблон напрямую. Но иногда было бы полезно иметь эту способность.
Например, я могу реализовать отсутствующий шаблон множественного наследования, используя интерфейсы и три таких класса:
public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }
public class First:IFirst
{
public void FirstMethod() { Console.WriteLine("First"); }
}
public class Second:ISecond
{
public void SecondMethod() { Console.WriteLine("Second"); }
}
public class FirstAndSecond: IFirst, ISecond
{
First first = new First();
Second second = new Second();
public void FirstMethod() { first.FirstMethod(); }
public void SecondMethod() { second.SecondMethod(); }
}
Каждый раз, когда я добавляю метод к одному из интерфейсов, мне нужно также изменить класс FirstAndSecond .
Есть ли способ внедрить несколько существующих классов в один новый класс, как это возможно в C ++?
Может быть, есть решение, использующее какую-то генерацию кода?
Или это может выглядеть так (воображаемый синтаксис c #):
public class FirstAndSecond: IFirst from First, ISecond from Second
{ }
Чтобы не было необходимости обновлять класс FirstAndSecond при изменении одного из интерфейсов.
РЕДАКТИРОВАТЬ
Возможно, было бы лучше рассмотреть практический пример:
У вас есть существующий класс (например, текстовый TCP-клиент на основе ITextTcpClient), который вы уже используете в разных местах внутри вашего проекта. Теперь вы чувствуете необходимость создания компонента вашего класса, чтобы он был легко доступен для разработчиков форм Windows.
Насколько я знаю, у вас есть два способа сделать это:
Напишите новый класс, который унаследован от компонентов и реализует интерфейс класса TextTcpClient, используя экземпляр самого класса, как показано в FirstAndSecond.
Напишите новый класс, который наследуется от TextTcpClient и каким-то образом реализует IComponent (на самом деле еще не пробовал).
В обоих случаях вам нужно выполнять работу по методу, а не по классу. Поскольку вы знаете, что нам понадобятся все методы TextTcpClient и Component, было бы самым простым решением просто объединить эти два в один класс.
Чтобы избежать конфликтов, это может быть сделано путем генерации кода, где результат может быть изменен впоследствии, но ввод этого вручную - чистая боль в заднице.
источник
Ответы:
C # и .net CLR не реализовали MI, потому что они еще не пришли к выводу, как они будут взаимодействовать между C #, VB.net и другими языками, а не потому, что «это сделает исходный код более сложным»
MI - это полезная концепция, вопросы без ответов: «Что вы делаете, когда у вас есть несколько общих базовых классов в разных суперклассах?
Perl - единственный язык, с которым я когда-либо работал, где MI работает и работает хорошо. .Net вполне может представить его однажды, но пока нет, CLR уже поддерживает MI, но, как я уже сказал, языковых конструкций для него еще нет.
До тех пор вы застряли с прокси-объектами и несколькими интерфейсами вместо этого :(
источник
Попробуйте использовать композицию вместо того, чтобы пытаться симулировать множественное наследование. Вы можете использовать интерфейсы, чтобы определить, какие классы составляют композицию, например:
ISteerable
подразумевает свойство типаSteeringWheel
,IBrakable
подразумевает свойство типаBrakePedal
и т. Д.Как только вы это сделаете, вы можете использовать функцию расширенных методов, добавленную в C # 3.0, чтобы еще больше упростить вызов методов для этих подразумеваемых свойств, например:
источник
myCar
, закончил ли рулевое управление перед вызовомStop
. Он может перевернуться, если егоStop
применить, когдаmyCar
скорость слишком высокая. : DЯ создал посткомпилятор C #, который включает такие вещи:
Вы можете запустить посткомпилятор как событие post-build Visual Studio:
В той же сборке вы используете это так:
В другой сборке вы используете это так:
источник
Вы можете иметь один абстрактный базовый класс, который реализует IFirst и ISecond, а затем наследовать только от этой базы.
источник
MI НЕ плох, все, кто (серьезно) использовал его, ЛЮБИТ его, и это НЕ усложняет код! По крайней мере, не больше, чем другие конструкции могут усложнить код. Плохой код - это плохой код независимо от того, присутствует ли на рисунке MI.
Во всяком случае, у меня есть милое небольшое решение для множественного наследования, которым я хотел поделиться, это по адресу; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog или вы можете перейти по ссылке в моем подписи ... :)
источник
myFoo
это типFoo
, который наследуется от,Moo
иGoo
оба из которых наследуютсяBoo
, то(Boo)(Moo)myFoo
и(Boo)(Goo)myFoo
не будет эквивалентен). Знаете ли вы о каких-либо подходах для сохранения идентичности?В моей собственной реализации я обнаружил, что использование классов / интерфейсов для MI, хотя и «в хорошей форме», имеет тенденцию вызывать чрезмерные сложности, поскольку вам нужно настроить все это множественное наследование только для нескольких необходимых вызовов функций, и в моем случае, нужно было сделать буквально в десятки раз излишне.
Вместо этого было проще сделать статические «функции, которые вызывают функции, которые вызывают функции» в различных модульных разновидностях, как своего рода замена ООП. Решением, над которым я работал, была «система заклинаний» для RPG, где эффекты должны сильно смешивать и сопоставлять вызовы функций, чтобы дать огромное разнообразие заклинаний без переписывания кода, во многом как показано в примере.
Большинство функций теперь могут быть статическими, потому что мне не обязательно нужен экземпляр для логики орфографии, тогда как наследование классов не может даже использовать виртуальные или абстрактные ключевые слова в то время как статические. Интерфейсы не могут использовать их вообще.
Кодирование кажется намного быстрее и чище, таким образом, IMO. Если вы просто выполняете функции и вам не нужны унаследованные свойства , используйте функции.
источник
С C # 8 теперь у вас практически есть множественное наследование через реализацию интерфейса по умолчанию:
источник
new ConsoleLogger().Log(someEception)
- это просто не будет работать, вам придется явно привести ваш объект к объекту,ILogger
чтобы использовать метод интерфейса по умолчанию. Так что его полезность несколько ограничена.Если вы можете жить с тем ограничением, что методы IFirst и ISecond должны взаимодействовать только с контрактом IFirst и ISecond (как в вашем примере) ... вы можете делать то, что вы просите, с помощью методов расширения. На практике это случается редко.
///
Таким образом, основная идея состоит в том, что вы определяете требуемую реализацию в интерфейсах ... этот необходимый материал должен поддерживать гибкую реализацию в методах расширения. В любое время вам нужно «добавить методы в интерфейс», вместо этого вы добавляете метод расширения.
источник
Да, использование интерфейса - это хлопотно, потому что каждый раз, когда мы добавляем метод в класс, мы должны добавлять сигнатуру в интерфейс. Кроме того, что если у нас уже есть класс с кучей методов, но нет интерфейса для него? мы должны вручную создать интерфейс для всех классов, которые мы хотим наследовать. И хуже всего то, что мы должны реализовать все методы в интерфейсах дочернего класса, если дочерний класс должен наследоваться от множественного интерфейса.
Следуя шаблону проектирования Фасада, мы можем имитировать наследование от нескольких классов, используя методы доступа . Объявите классы как свойства с {get; set;} внутри класса, который должен наследоваться, и все открытые свойства и методы принадлежат этому классу, а в конструкторе дочернего класса создайте экземпляры родительских классов.
Например:
с этой структурой класс Child будет иметь доступ ко всем методам и свойствам Class Father и Mother, имитирующим множественное наследование, наследующим экземпляр родительских классов. Не совсем то же самое, но это практично.
источник
Quick Actions and Refactorings...
это необходимо знать, это сэкономит вам много времениМы все, кажется, идем по пути интерфейса с этим, но очевидная другая возможность здесь - это сделать то, что должен делать ООП, и построить ваше дерево наследования ... (разве это не то, что дизайн класса - это все около?)
Эта структура обеспечивает многократно используемые блоки кода и, конечно, как должен быть написан код ООП?
Если этот конкретный подход не совсем соответствует требованиям, мы просто создаем новые классы на основе требуемых объектов ...
источник
Множественное наследование - это одна из тех вещей, которая обычно вызывает больше проблем, чем решает. В C ++ это соответствует схеме предоставления вам достаточного количества веревки, чтобы повеситься, но Java и C # решили пойти по более безопасному пути, не давая вам выбора. Самая большая проблема состоит в том, что делать, если вы наследуете несколько классов, у которых есть метод с той же сигнатурой, которую наследник не реализует. Какой метод класса он должен выбрать? Или это не должно компилироваться? Как правило, существует другой способ реализации большинства вещей, который не основан на множественном наследовании.
источник
Если X наследует от Y, это имеет два несколько ортогональных эффекта:
Хотя наследование обеспечивает обе функции, нетрудно представить обстоятельства, при которых один из них может быть полезен без другого. Ни один из известных мне языков .net не имеет прямого способа реализации первого без второго, хотя такую функциональность можно получить, определив базовый класс, который никогда не используется напрямую, и имея один или несколько классов, которые наследуются напрямую от него, не добавляя ничего new (такие классы могут совместно использовать весь свой код, но не могут заменять друг друга). Однако любой CLR-совместимый язык позволит использовать интерфейсы, которые предоставляют вторую функцию интерфейсов (заменяемость) без первой (повторное использование элемента).
источник
я знаю, я знаю, хотя это не разрешено и так далее, иногда вам это действительно нужно для тех:
как в моем случае я хотел сделать этот класс b: Form (да, windows.forms) класс c: b {}
потому что половина функции была идентична и с интерфейсом вы должны переписать их все
источник
class a : b, c
(реализация любых необходимых дыр в контракте). Возможно, ваши примеры просто упрощены?Поскольку время от времени возникает вопрос множественного наследования (MI), я хотел бы добавить подход, который решает некоторые проблемы с шаблоном композиции.
Я дописывать
IFirst
,ISecond
,First
,Second
,FirstAndSecond
подход, так как он был представлен в этом вопросе. Я сократил пример кода доIFirst
, так как шаблон остается неизменным независимо от количества интерфейсов / базовых классов MI.Предположим, что с MI
First
иSecond
оба будут происходить из одного базового классаBaseClass
, используя только открытые элементы интерфейса изBaseClass
Это можно выразить, добавив ссылку на контейнер
BaseClass
в реализациюFirst
andSecond
:Все становится более сложным, когда на защищенные элементы интерфейса
BaseClass
ссылаются или когдаFirst
иSecond
они будут абстрактными классами в MI, требуя, чтобы их подклассы реализовали некоторые абстрактные части.C # позволяет вложенным классам получать доступ к защищенным / закрытым элементам их содержащих классов, так что это можно использовать для связывания абстрактных битов из
First
реализации.Это довольно сложный пример, но если фактическая реализация FirstMethod и SecondMethod достаточно сложна, а количество доступных закрытых / защищенных методов умеренное, то этот шаблон может помочь преодолеть отсутствие множественного наследования.
источник
Это соответствует ответу Лоуренса Уэнама, но в зависимости от вашего варианта использования это может быть или не быть улучшением - вам не нужны сеттеры.
Теперь любой объект, который знает, как получить человека, может реализовать IGetPerson, и он автоматически будет иметь методы GetAgeViaPerson () и GetNameViaPerson (). С этого момента в основном весь код Person идет в IGetPerson, а не в IPerson, кроме новых ivars, которые должны входить в оба. И при использовании такого кода вам не нужно беспокоиться о том, действительно ли ваш объект IGetPerson фактически является IPerson.
источник
Теперь это возможно через
partial
классы, каждый из которых может наследовать класс самостоятельно, в результате чего конечный объект наследует все базовые классы. Вы можете узнать больше об этом здесь .источник