Какие есть хорошие альтернативы множественному наследованию в .NET?

у меня возникла небольшая проблема с моей иерархией классов в приложении WPF. Это'Это одна из тех проблем, когда у вас есть два дерева наследования, и вы можетенайти логичный способ, чтобы ваше наследство работало гладкобез множественное наследование. Я'Мне интересно, есть ли у кого-нибудь блестящие идеи для того, чтобы заставить работать такую систему, не делая невозможным ее отслеживание или отладку.

Я инженер низкого уровня, поэтому моя первая мысль всегда "Ой! Я'Я просто напишу некоторые из этих классов на нативном C ++, и буду ссылаться на них внешне! Тогда я смогу повеселиться на старой школе! " Увы это непомогает, когда вам нужно наследовать от управляемого элемента управления ...

Позвольте мне показать фрагмент моей текущей проектной диаграммы классов:

 ____________________________________      _____________________________________
| CustomizableObject                 |    | System.Windows.Controls.UserControl |
|____________________________________|    |_____________________________________|
|   string XAMLHeader()              |                        ▲
|   string XAMLFooter()              |◄--┐                    |
|   CustomizableObject LoadObject()  |   \                    |
|    |    \                   |
|____________________________________|     \                  |
         ▲                      ▲           \                 |
         |                      |            \                |
         |                      |             \               |
 _________________    ______________________   \    _____________________
| SpriteAnimation |  | SpriteAnimationFrame |  └---| CustomizableControl |
|_________________|  |______________________|      |_____________________|
                                                      ▲             ▲
                                                      |             |
                                                      |             |
                                                  ________    _____________
                                                 | Sprite |  | SpriteFrame |
                                                 |________|  |_____________|

Проблема довольно ясна: разделение деревьев объектов CustomizableObject и CustomizableControl --- и вставка UserControl в одно, но не в оба дерева.

Не имеет практического смысла перемещать реализацию CustomizableObject в его производные классы, так как реализация не 'т варьируется в зависимости от класса. Кроме того, было бы очень сложно реализовать его несколько раз. Так что я действительно нене хочу сделать CustomizableObject интерфейсом. Интерфейсное решение нене имеет никакого смысла для меня. (Интерфейсы имеютникогда действительно, для меня было много смысла, если честно ...)

Так что еще раз говорю, у кого-нибудь есть яркие идеи? Вот этот'настоящий рассол. Я'Я хотел бы узнать больше о том, как заставить интерфейсы работать с моим деревом объектов, а не против него. Я'Я делаю этот простой движок спрайтов, используя WPF и C # как серьезное упражнение, больше всего на свете. Это очень легко решить в C ++, но мне нужно выяснить, как решить эти проблемы в управляемой среде, вместо того, чтобы бросать руки в воздух и бегать обратно к Win32, когда дела идут плохо.

 Kamarey17 авг. 2009 г., 21:06
+1 за рисование всего этого :)
 Stefan Steinegger04 апр. 2013 г., 08:57
Этот вопрос нене вписывается в стекопотоки Q &Формат. "Какие есть хорошие альтернативы? уже предполагает, что нет единого правильного ответа. Там много разных способов и мнений.
 Giffyguy17 авг. 2009 г., 23:27
Хаха яЯ рад, что мои работы могут быть оценены.

Ответы на вопрос(5)

используемая во многих языках для добавления функциональности в класс во время выполнения. Mixins хорошо известны во многих языках. Фреймворк для повторного микширования - это фреймворк, который переносит технологию mixin в .NET.

Идея проста: украсьте свой класс атрибутом класса, представляющим миксин. Во время выполнения эта функциональность будет добавлена в ваш класс. Таким образом, вы можете эмулировать множественное наследование.

Решением вашей проблемы может быть создание CustomizableObject для миксина. Для этого вам понадобится фреймворк для повторного микширования.

[Использует (CustomizableObjectMixin)] CustomizableControl: UserControl

Пожалуйста, проверьте remix.codeplex.com для более подробной информации.

CustomizableObject просто кричит о превращении в интерфейс (и поскольку каждый конкретный тип может быть преобразован в объект, эта часть имени является избыточной). Проблема, которую выэто то, что тыВы не уверены, как сохранить некоторую базовую логику, которая будет совместно использоваться или будет незначительно отличаться в зависимости от реализации, и вы хотите сохранить эту логику в самом дереве, чтобы она работала полиморфно (это слово?).

Вы можете достичь этого через делегатов. Я'Я не уверен, какие именно члены доставляют вам неприятности, но, возможно, что-то вроде этого:

 ____________________________________      _____________________________________
| ICustomizable                      |    | System.Windows.Controls.UserControl |
|                                    |    |_____________________________________|
|   Func<string> XAMLHeader;         |                        ▲
|   Func<string> XAMLFooter          |◄--┐                    |
|   ICustomizabl LoadObject() |   \                    |
|   <possible other="" implementations=""> |    \                   |
|____________________________________|     \                  |
         ▲                      ▲           \                 |
         |                      |            \                |
         |                      |             \               |
 _________________    ______________________   \    _____________________
| SpriteAnimation |  | SpriteAnimationFrame |  └---| CustomizableControl |
|_________________|  |______________________|      |_____________________|
                                                      ▲             ▲
                                                      |             |
                                                      |             |
                                                  ________    _____________
                                                 | Sprite |  | SpriteFrame |
                                                 |________|  |_____________|
</possible></string></string>

Кроме того, у вас, скорее всего, есть какая-то действительно логичная логика, которую вы чувствуете по-настоящемупринадлежит с вашим типом CustomizableObject. Но это, вероятно, неверно: вы создали тип с намерением использовать этот тип в конкретной ситуации. Например, из контекста это выглядит как тыЯ буду создавать эти элементы управления и анимации и использовать их в форме Windows. Нужно иметь свою собственную форму, которая наследуется от базового System.Windows.Form, и этот новый тип формы должен знать о ICustomizableObject и о том, как его использовать. Тот'где ваша статическая логика будет идти.

Это кажется немного неловким, но этодоказал свою точность, когда вы решите изменить механизмы представления. Что произойдет, если вы перенесете этот код в WPF или Silverlight? Oни'Скорее всего, вам придется использовать код реализации немного по-другому, чем Windows Forms, и выВам все еще, вероятно, придется изменить ваши реализации CustomizableControl. Но ваша статическая логика теперь все в правильном месте.

Наконец, метод LoadObject (), который выИспользование, как мне кажется, не в том месте. Вы'Мы говорим вам, что вы хотите, чтобы каждый настраиваемый тип предоставлял метод, который вы можете вызвать, который знает, как загрузить / создать себя. Но это действительно что-то другое. Возможно, вы захотите еще один интерфейс с именемIConstructable и ваш настраиваемый тип IC реализует это (IConstructable).

 Giffyguy17 авг. 2009 г., 22:18
Должен сказать, мне очень нравится подход делегатов. На самом деле я потратил некоторое время на программирование, пытаясь понять, как делегаты будут работать для этого. Я планировал принять ваш ответ, как только я получил его для компиляции и запуска - методы расширения работали лучше, и я думаю, что он стал чище. Тем не менее, это определенно отличное решение, заслуживающее внимания. На самом деле, яГотов поспорить, есть много случаев, когда делегаты решали бы проблему даже лучше, чем методы расширения.
 Giffyguy17 авг. 2009 г., 21:14
+1 Хмммм ... вы подняли очень интересный вопрос. Я действительно хотел бы, чтобы мои элементы управления спрайтом были автономными. В конце концов, это WPF, так что я, наверное, не долженЯ просто наследую от базового типа окна, как мы это делали в WinForms, - но я мог бы легко применить это решение, скажем, к моим элементам управления SpriteLayer или SpriteView.

используйте интерфейсы или используйте композицию. Честно говоря, интерфейсы очень мощные, и после прочтения этой строки

Интерфейсное решение нене имеет никакого смысла для меня. (Интерфейсы никогда не имели большого смысла для меня, если честно ...)

Я думаю, что вы должны научиться правильно их использовать. Тем не менее, если существует просто какая-то логика, которая нужна нескольким классам, но для этих классов не имеет смысла наследовать один и тот же базовый класс, просто создайте класс для инкапсуляции этой логики и добавьте переменную-член этого класса в ваши классы. которые доставляют вам проблемы. Таким образом, все классы содержат логику, но могут быть отдельными в своих иерархиях наследования. Если классы должны реализовывать общий интерфейс (ы), используйте интерфейсы.

 Giffyguy17 авг. 2009 г., 20:59
Мне очень нравится, как вы описываете это. Кажется янаверно примусостав" маршрут. Это'Все усложнит, но не так, как повсеместно мои реализации. (Ха-ха, яЯ такой упрямый ... Я набираюсь смелости, чтобы напрямую обратиться к SO за помощью с интерфейсами, и я ВСЕ ЕЩЕ могу »не могу проглотить мою гордость и принять все советы о силе интерфейсов.)
Решение Вопроса

рфейсом для обеспеченияпроизводный класс " реализация, очень похожая на System.Linq.Queryable:

interface ICustomizableObject
{
    string SomeProperty { get; }
}

public static class CustomizableObject
{
    public static string GetXamlHeader(this ICustomizableObject obj)
    {
        return DoSomethingWith(obj.SomeProperty);
    }

    // etc
}

public class CustomizableControl : System.Windows.Controls.UserControl, ICustomizableObject
{
    public string SomeProperty { get { return "Whatever"; } }
}

Использование: Пока у вас есть директива using для (или находящаяся в том же пространстве имен, что и) пространство имен, в котором определены ваши методы расширения:

var cc = new CustomizableControl();
var header = cc.GetXamlHeader();
 dahlbyk17 авг. 2009 г., 22:15
Рад помочь! В качестве дополнения, вы можете рассмотреть возможность явной реализации интерфейса в CustomizableControl, чтобы эти члены были только "видимый» когда элемент управления фактически используется в своем настраиваемом качестве.
 dahlbyk17 авг. 2009 г., 21:17
Добавлен пример использования для вас. Пока компилятор может их найти, методы расширения выглядят так же, как методы экземпляра. К сожалению, нет расширенных свойств, поэтому я переименовал метод в соответствии с соглашениями об именах методов.
 Giffyguy17 авг. 2009 г., 22:38
Я также добавлю, что это решение может быть реализовано чисто - качество, которое, я считаю, стало редким в некоторых более современных менталитетах .NET.
 dahlbyk17 авг. 2009 г., 21:22
Кроме того, проверьте эту статью на MSDN:msdn.microsoft.com/en-us/vcsharp/bb625996.aspx
 dahlbyk17 авг. 2009 г., 23:39
Несколько замечательных моментов. Методы расширения действительно ломаются, если вам требуется общее состояние, которое неt, предоставляемый интерфейсом, так что планируйте заранее! Чтобы узнать больше о реализации интерфейсов по умолчанию (и более), посмотрите это видео на канале 9: от эксперта к эксперту: Эрик Мейер и Андерс Хейлсберг - будущее C #channel9.msdn.com/shows/Going+Deep/...
 Daniel Earwicker17 авг. 2009 г., 23:25
Просто чтобы немного поспать на параде, есть несколько недостатков по сравнению с полным MI, как это может быть в .NET. Во-первых, вы можете• развивать интерфейс без нарушения бинарной совместимости (обычно это не проблема для библиотек с небольшим сообществом пользователей). Это может быть исправлено в некоторых будущих версиях CLR, где они могут позволить интерфейсным методам иметь реализацию по умолчанию. Во-вторых, в MI каждый из множества базовых классов может добавлять поля данных в объединенный класс. Вы'Я должен был смоделировать это с помощью методов расширения с каким-то потоком локального беспорядка статического словаря.
 Giffyguy17 авг. 2009 г., 22:03
Это решение отлично решает проблему! Это действительно чертовски прохладно. Преимущество здесь в том, что я могу хранить реализацию в одном месте, в своем дереве объектов - вместо того, чтобы распространять его по полиморфам или с помощью интерфейсов - так что он прекрасно работает с менталитетом управления WPF. Я рекомендую это решение всем, у кого проблемы с множественным наследованием / интерфейсом в .NET
 Giffyguy17 авг. 2009 г., 23:37
@Earwicker: Вы поднимаете отличные очки. Ваш комментарий прав на деньги, учитывая, что этот вопрос спрашивает об общих альтернативах MI в .NET. Это решение имеет большой потенциал в некоторых случаях, и яЯ склонен в его пользу, потому что он так хорошо решил мою конкретную проблему. Но пока мыОбсуждая это, я должен сказать, что я действительно разочарован в Microsoft здесь. Фреймворк поддерживает полный OO, и они явно незаботятся о незначительных хитах производительности. Так почемуTI MI, где это имеет смысл? System.Object всегда создает ромбовидную форму, но виртуальное наследование решает эту проблему.

Я думаю, что это одно из решений.

public interface IClassA
{
    void Foo1();
    void Foo2();
}

public interface IClassB
{
    void Foo3();
    void Foo4();
}

public class ClassA :IClassA
{
    #region IClassA Members

    public void Foo1()
    {
    }

    public void Foo2()
    {
    }

    #endregion
}

public class ClassB :IClassB
{
    #region IClassB Members

    public void Foo3()
    {
    }

    public void Foo4()
    {
    }

    #endregion
}

public class MultipleInheritance :IClassA, IClassB
{
    private IClassA _classA;
    private IClassB _classB;

    public MultipleInheritance(IClassA classA, IClassB classB)
    {
        _classA = classA;
        _classB = classB;
    }

    public void Foo1()
    {
        _classA.Foo1();
    }

    public void Foo2()
    {
        _classA.Foo2();
        AddedBehavior1();
    }

    public void Foo3()
    {
        _classB.Foo3();
        AddedBehavior2();
    }

    public void Foo4()
    {
        _classB.Foo4();
    }

    private void AddedBehavior1()
    {

    }

    private void AddedBehavior2()
    {

    }
}

Класс MultipleInheritance добавляет новое поведение к двум различным объектам, не влияя на поведение этих объектов. Множественное наследование имеет то же поведение, что и доставленные объекты (наследует оба поведения).

 John Saunders04 апр. 2013 г., 04:57
Я нене вижу этого в качестве шаблона декоратора.
 julealgon07 янв. 2014 г., 14:33
Я думаю, что это правильный и возможный подход внемного контексты, я нене понимаю, почему вы были отклонены. Очевидно, этоЭто не общий подход, поскольку декораторы предназначены для расширения существующих классов. Хотя вы можете напрямую использовать тип декоратора и добавить к нему совершенно новые методы. я могу видетьтот как не образец декоратора, но он может работать как вариация композиции.

Ваш ответ на вопрос