Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Множественное наследование в C#

4.9K
07 декабря 2008 года
efferson
57 / / 08.12.2005
День добрый :)

Как известно множественное наследование в таких языках как Java и С# отсутствует, т.к. является потенциальным источником ошибок. Вместо него разработчику предлагается использовать механизм интерфейсов. Однако, интерфейсы могут хранить только прототипы методов, но не их код.

Как в таком случае они могут заменить множественное наследование? Допустим у меня есть два класса - A и B. Я хочу создать классы C, D и E. Каждый из этих классов должен включать в себя методы как класса A, так и класса B. На сколько я понял логику интерфейсов, для реализации моей задумки мне придётся описать два интерфейса - IA и IB. После чего объявить, что класс A реализует интерфейс IA, класс B - IB, а классы C, D и E оба этих интерфейса сразу. Всё бы хорошо, но только код функций каждого интерфейса придётся воспроизводить в каждом классе заново.

Я ничего не перепутал? :)
288
07 декабря 2008 года
nikitozz
1.2K / / 09.03.2007
Цитата: efferson

Я ничего не перепутал? :)



Не совсем. Стопроцентно можно избежать дублирования кода. Просто классы A, B, C, D, E немного абстрактны, для построения правильной иерархии. Если приведете конкретный пример, будет легче.

341
07 декабря 2008 года
Der Meister
874 / / 21.12.2007
Цитата:
Допустим у меня есть два класса - A и B. Я хочу создать классы C, D и E. Каждый из этих классов должен включать в себя методы как класса A, так и класса B. На сколько я понял логику интерфейсов, для реализации моей задумки мне придётся описать два интерфейса - IA и IB. После чего объявить, что класс A реализует интерфейс IA, класс B - IB, а классы C, D и E оба этих интерфейса сразу. Всё бы хорошо, но только код функций каждого интерфейса придётся воспроизводить в каждом классе заново.

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

5
07 декабря 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: Der Meister
Эта гипотетическая ситуация является лишь гипотетической. В нормальной системе этого не нужно. Не забывайте, что тип - не только контейнер для данных, но и некий набор ответственностей над ними.


Угу. Кое чего можно сделать через генерики. Максимально эффективная связка в C# это генерики + интерфейсы. Хотя мне их выразительной возможности метами нехватает, приходится отражать.

4.9K
08 декабря 2008 года
efferson
57 / / 08.12.2005
Например, у нас есть два класса - лошадь и птица:
Код:
class CHorse
{
    public void ride()
    {
        // код
    }
}

class CBird
{
    public void fly()
    {
        // код
    }
}


Теперь, как мне спарить этих двух зверушек, что бы получился Пегас, которой умеет скакать и летать, без дублирования кода соответствующих методов? На C++ это выглядело бы так:
 
Код:
class CPegas : public CHorse, public CBird
{
}


А как это будет выглядеть при использовании интерфейсов?
35K
08 декабря 2008 года
m3f
17 / / 31.08.2008
Код:
class CPegas
{
     public CPegas()
     {
          __Horse = new CHorse();
          __Bird = new CBird();
     }
     
     private CHorse __Horse;
     private CBird __Bird;

     public void ride()
     {
        __Horse.ride();
     }
     
     public void fly()
     {
         __Bird.fly();
     }
     //...
}
5
08 декабря 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: m3f
 
Код:
class CPegas ...


Несовсем корректно.

Вот стандартный подход. Pegasus - композитный объект, содержит в себе Bird и Horse.

Код:
public interface IHorse {
    void Ride();
}

public interface IBird {
    void Fly();
}

public class Horse : IHorse {
    public void Ride() {
    }
}

public class Bird : IBird {
    public void Fly() {
    }
}

public interface IPegasus : IHorse, IBird {
}

public class Pegasus : IPegasus {
    private Horse _horse = new Horse();

    private Bird _bird = new Bird();
   
    public void Ride() {
        _horse.Ride();
    }

    public void Fly() {
        _bird.Fly();
    }
}

Интерфейсы нам нужны для поддержки иерархии наследования.
341
08 декабря 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=efferson]Теперь, как мне спарить этих двух зверушек, что бы получился Пегас, которой умеет скакать и летать, без дублирования кода соответствующих методов?[/QUOTE]Ну тут Джесс Либерти чувствуется. Он же и приводит правильную реализацию (ту, что предоставил hardcase).
Ещё раз: не забывайте об ответственностях. С концептуальной точки зрения, птица - не объект с крыльями, а целая система, в которой все элементы находятся во взаимосвязи. У пегаса взаимосвязь элементов (в частности, зависимость системы полёта от других его частей) может (и даже должна) быть иной.
Кстати, C# предлагает механизм явной реализации интерфейсов. Можно заставить пегаса летать как в своём стиле, так и в стиле птицы (пральней будет "как летать в стиле пегаса, так и просто летать").
5
09 декабря 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: Der Meister
Кстати, C# предлагает механизм явной реализации интерфейсов...

Вспоминается тот самый Ping с его диспоузом :D

341
09 декабря 2008 года
Der Meister
874 / / 21.12.2007
Цитата: hardcase
Вспоминается тот самый Ping с его диспоузом :D


Мне тоже кажется, что эта возможность имеет сомнительный характер: ни разу мне не пригодилась. Но я допускаю, что могу просто чего-то не знать или банально не понимать. Как бы я вижу, что .NET умные дядьки сочиняли, и введение подобной двусмысленности должно иметь под собой какую-то почву, наверное. Можно и обсудить.
Предполагаемые мной предпосылки:
- принцип подстановки Барбары Лискофф в формулировке Роберта Мартина: методы, принимающие в качестве параметра указатели и ссылки на объекты базового класса, должны иметь возможность использовать эти объекты без необходимости знать, к какому классу (базовому или любому из производных) они принадлежат (один из базовых принципов ООП-дизайна);
- видимо, даже при тщательном планировании дизайна, средствами обычного полиморфизма (возможно именно полиморфизма .NET, хотя разницы не вижу) не всегда можно обеспечить выполнение вышеприведённого принципа;
- возможно, это имеет какое-то отношение к разрешению конфликтов имён методов реализуемых интерфейсов.
Лично мне кажется, что необходимость применения явной реализации интерфейсов в последних двух случаях обусловлена неверным дизайном. Кто-нибуть встречал инциденты подобного?

341
09 декабря 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=Der Meister]Но я допускаю, что могу просто чего-то не знать или банально не понимать.[/QUOTE]Поясню дополнительно.[QUOTE=Der Meister]Лично мне кажется, что необходимость применения явной реализации интерфейсов в последних двух случаях обусловлена неверным дизайном.[/QUOTE]Фактически, принцип подстановки задаёт определение суперкласса. Если класс следующего уровня иерархии не может обеспечить выполнение этого принципа, то он не имеет права участвовать в этой иерархии. Может быть, с интерфейсами как-то иначе обстоят дела?
5
11 декабря 2008 года
hardcase
4.5K / / 09.08.2005
Цитата: Der Meister
- возможно, это имеет какое-то отношение к разрешению конфликтов имён методов реализуемых интерфейсов

Пожалуй, это единственный убедительный аргумент.
К примеру, этот способ используется при реализации параметрического интерфейса IEnumerable<T>. При этом нужно реализовывать два метода GetEnumerator - строго типизированный и нестрогий.

С моей точки зрения, конкретно интерфейсов (в .NET-ной трактовке) недостаточно. Мне иногда остро недостает подобия контрактов - своего рода "интерфейсов времени компиляции". Когда мы декларируем компилятору, что вот данный объект должен поддерживать такие-то и такие-то свойства-методы. Обосновано это тем, что зачастую не важен конкретный тип объекта - важен сам факт наличия у объекта набора тех или иных свойств-методов. Замечу, что полиморфизм такого уровня невозможен в C#, но вполне реализуем в Nemerle.

341
12 декабря 2008 года
Der Meister
874 / / 21.12.2007
[QUOTE=hardcase]Пожалуй, это единственный убедительный аргумент.
К примеру, этот способ используется при реализации параметрического интерфейса IEnumerable<T>. При этом нужно реализовывать два метода GetEnumerator - строго типизированный и нестрогий.[/QUOTE]Я тоже сёдня об этом подумал, и именно об IEnumerable :) Да, например пара методов
 
Код:
int Method()
{
}
и
double Method()
{
}
не попадают под определение перегруженных, и компилятор такой код пропустить не должен: списки принимаемых обоими методами параметров совпадают (оба пусты). Но это, пожалуй, и всё...[QUOTE=hardcase]С моей точки зрения, конкретно интерфейсов (в .NET-ной трактовке) недостаточно. Мне иногда остро недостает подобия контрактов - своего рода "интерфейсов времени компиляции". Когда мы декларируем компилятору, что вот данный объект должен поддерживать такие-то и такие-то свойства-методы. Обосновано это тем, что зачастую не важен конкретный тип объекта - важен сам факт наличия у объекта набора тех или иных свойств-методов. Замечу, что полиморфизм такого уровня невозможен в C#, но вполне реализуем в Nemerle.[/QUOTE]Действительно. Например, универсальную абстрактную фабрику средствами C# не сделать: всегда нужна заточка под конкретный тип продукта. С одной стороны - плохо. С другой стороны, может оказаться, что одноимённые методы разных по природе типов дают совершенно разный эффект, и различия этих методов могут дать труднолокализуемые последствия: например, Class1.Method() влияет только на Class1, а Class2.Method() создаёт ещё и экземпляр типа Class3, используемый в ассоциации с типом Class4. А метод класса-параметра с именем Method() используется в параметризованном классе более чем в одном месте... Утрирую, конечно, но парадигмой C# подобные ошибки допустить будет легко.
Шаблоны STL, например, на соглашениях о реализуемых классами методах зиждятся. Забыл констуктор копий - пеняй на себя :)
58K
05 марта 2010 года
hps
2 / / 05.03.2010
Ну раз тут всё так описано, то вот у меня как раз по теме вопрос:
Есть базовый класс

public abstract Objectbase{}

public abstract ObjectManagerBase<T> where T : ObjectBase, new()
{

public abstract T Create(DataRow dr);
...
}

Наследники ObjectManagerBase<T>, например UserManager<User>, управляют объектами User, не имеющими языковой поддержки,
но имеются так же объекты типа Category, логика создания которых идетнична User, но для каждой страны у них своё имя и ещё кое какие специфичные для данной страны свойства.
Для создания объекта LanguageSupport торчащего через свойство Category.LanguageSupport есть соответсвующий метод,
который приходится дублировать во всех наследниках ObjectManagerBase<T>, управляющий объектами с языковой поддержкой.

По идее было бы замечательно создать ещё один базовый класс типа

public abstract LanguageSupportedOjectManagerBase<T> where T : LanguageSupport, new()
{

public T Create(DataRow dr)
{
T languageSupport = new T();
...
return languageSupport;
}
}

и создать класс CategoryManager, наследующий от ObjectManagerBase<T> и LanguageSupportedOjectManagerBase<T>, но так в .net нельзя.

Возможно ли добавть функционал базового класса LanguageSupportedOjectManagerBase<T> к некоторым наследникам ObjectManagerBase<T>?
5
05 марта 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: hps

По идее было бы замечательно создать ещё один базовый класс типа

public abstract LanguageSupportedOjectManagerBase<T> where T : LanguageSupport, new()
{

public T Create(DataRow dr)
{
T languageSupport = new T();
...
return languageSupport;
}
}

и создать класс CategoryManager, наследующий от ObjectManagerBase<T> и LanguageSupportedOjectManagerBase<T>, но так в .net нельзя.

Возможно ли добавть функционал базового класса LanguageSupportedOjectManagerBase<T> к некоторым наследникам ObjectManagerBase<T>?


Вам сюда: паттерн "Декоратор".
Для того чтобы можно было различать объекты с языковой поддержкой неплохо было бы завести интерфейс вроде ILanguageSupported.

з.ы. хотя возможно я чего-то не понял в вопросе.

58K
05 марта 2010 года
hps
2 / / 05.03.2010
Цитата: hardcase
Вам сюда: паттерн "Декоратор".
Для того чтобы можно было различать объекты с языковой поддержкой неплохо было бы завести интерфейс вроде ILanguageSupported.

з.ы. хотя возможно я чего-то не понял в вопросе.



Да, слышал про такой паттерн, но не вникал, надо будет вникнуть.

По поводу интерфейса:

как я понимаю предполагается интерфейс типа такого

 
Код:
ILanguageSupported<T> where T : LanguageSupport, new()
{
    T CreateLanguageSupport(DataRow dt);
}


такой вариант не очень, так как придётся реализовывать его во всех классах с языковой поддержкой,
а реализация везде будет один в один.
5
05 марта 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: hps

как я понимаю предполагается интерфейс типа такого

 
Код:
ILanguageSupported<T> where T : LanguageSupport, new()
{
    T CreateLanguageSupport(DataRow dt);
}
такой вариант не очень, так как придётся реализовывать его во всех классах с языковой поддержкой,
а реализация везде будет один в один.

Зависит от размера кода:
Вариант 1: захардкодить вручную.
Вариант 2: сделать генератор (например посредством XSLT).
Вариант 3: сделать рантайм-генератор через Emit или CodeDom.
Вариант 4: использовать что-то типа PostSharp.
Вариант 5: использовать Nemerle и макроатрибуты.

Я использовал бы вариант №5 как наиболее простой и удобный.

9.7K
30 декабря 2011 года
Vitamant
228 / / 07.02.2011
Добавлю свои пять копеек.
Множественное наследование методов возможно.

Надеюсь, это не страшная тайна (потому что нигде ничего подобного не видел, и "изобрел" сам), но вещь действительно жуткая, к использованию не рекомендующаяся. Но мне вот понадобилась. Написал. Потом понял, что написал. Ужаснулся и решил поделиться. :)

Код:
public interface IHorse
    {
    }

    public interface IBird
    {
    }

    public interface IPegasus : IHorse, IBird
    {
    }

    public static class InterfaceExtensions
    {
        public static void Ride(this IHorse horse)
        {
            Console.WriteLine("Я скачу!");
        }

        public static void Fly(this IBird bird)
        {
            Console.WriteLine("Я лечу!");
        }
    }

    public class Pegas : IPegasus
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            Pegas pegas = new Pegas();
            pegas.Ride();
            pegas.Fly();
            Console.ReadLine();
        }
    }


В итоге получаем нечто монструозное, наподобие шаблонов из С++
341
30 декабря 2011 года
Der Meister
874 / / 21.12.2007
Цитата: Vitamant
Ужаснулся и решил поделиться. :)

Что ужасного в том, чтобы использовать метод расширения по назначению (т. е. для реализации кастрированных примесей)?
Однако методы расширения не имеют отношения ни к множественному наследованию, ни к шаблонам. Например потому, что нет возможности расширить набор полей/свойств/событий типа, а полиморфизм на уровне перегрузок и выбора между методом типа и методом расширения на этапе компиляции полиморфизмом не считается.
Покажите лучше, зачем вам понадобилось множественное наследование.

9.7K
30 декабря 2011 года
Vitamant
228 / / 07.02.2011
Цитата: Der Meister
Что ужасного в том, чтобы использовать метод расширения по назначению (т. е. для реализации кастрированных примесей)?
Однако методы расширения не имеют отношения ни к множественному наследованию, ни к шаблонам. Например потому, что нет возможности расширить набор полей/свойств/событий типа, а полиморфизм на уровне перегрузок и выбора между методом типа и методом расширения на этапе компиляции полиморфизмом не считается.
Покажите лучше, зачем вам понадобилось множественное наследование.


Рисую облочку над WinAPI для замены AutoIT. Поскольку все элементы являются окнами, но не все "окна" способны адекватно реагировать на различные сообщения, несколько из них я выделил в интерфейсы с расширенными методами. Тем не менее, уже отошел от них, сделав человеческое древовидное наследование, а недоступные методы перекрыл приватными загулшками. Так спокойнее. :)

297
03 января 2012 года
koodeer
1.2K / / 02.05.2009
Цитата: hardcase
Вариант 1: захардкодить вручную.
Вариант 2: сделать генератор (например посредством XSLT).
Вариант 3: сделать рантайм-генератор через Emit или CodeDom.
Вариант 4: использовать что-то типа PostSharp.
Вариант 5: использовать Nemerle и макроатрибуты.


hardcase, [COLOR="gray"]будь ты неладен со своими советами :),[/COLOR] не поделишься годными ссылками по варианту №2 - генерация кода посредством XSLT?
И почему в списке нет T4?

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог