What are some good alternatives to multiple-inheritance in .NET?
Asked Answered
V

6

38

I've run into a bit of a problem with my class hierarchy, in a WPF application. It's one of those issues where you have two inheritance trees merging together, and you can't find any logical way to make your inheritance work smoothly without multiple inheritance. I'm wondering if anyone has any bright ideas for getting this kind of system working, without making it impossible to follow or debug.

I'm a low-level engineer, so my first thought is always, "Oh! I'll just write some of these classes in native C++, and reference them externally! Then I can have all my old-school OO fun!" Alas, this doesn't help when you need to inherit from managed controls...

Allow me to show a snippet of my current projected class diagram:

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

The problem is pretty clear: the separation of the CustomizableObject and CustomizableControl object trees --- and the insertion of UserControl into one, but not both, of the trees.

It makes no practical sense to move the implementation of CustomizableObject into its derived classes, since the implementation doesn't vary by class. Furthermore, it would be really confusing to have it implemented multiple times. So I really don't want to make CustomizableObject an interface. The interface solution doesn't make any sense to me. (Interfaces have never really made much sense to me, to be honest...)

So I say again, anyone have any bright ideas? This one's a real pickle. I'd like to learn more about making interfaces work WITH my object tree, rather than against it. I'm making this simple sprite engine using WPF and C# as a solid exercise, more than anything. This is so easy to solve in C++ - but I need to figure out how to solve these problems in a managed environment, rather than throwing my hands in the air and running back to Win32 whenever the going gets tough.

Verina answered 17/8, 2009 at 18:47 Comment(3)
+1 for drawing all these:)Worcestershire
Haha, I'm glad my artwork can be appreciated.Verina
This question doesn't fit well into stackoverflows Q&A format. "What are some good alternatives" already assumes that there is no single correct answer. There a many different ways and opinions.Newsstand
D
19

One approach is to use extension methods with an interface to provide your "derived class" implementation, much like 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"; } }
}

Usage: As long as you have a using directive for (or are in the same namespace as) the namespace where your extension methods are defined:

var cc = new CustomizableControl();
var header = cc.GetXamlHeader();
Diplegia answered 17/8, 2009 at 19:0 Comment(8)
Added a usage example for you. As long as the compiler can find them, extension methods look and feel just like instance methods. No extension properties, unfortunately, thus my renaming the method to follow method naming conventions.Diplegia
Also, check out this article on MSDN: msdn.microsoft.com/en-us/vcsharp/bb625996.aspxDiplegia
This solution solves the problem perfectly!! That is really freakin' cool. The bonus here is that I get to keep the implementation in a single location, in my object tree - instead of spreading it around polymorphically, or with interfaces - so it works great with the WPF control mentality. I recommend this solution to anyone who is having multiple-inheritance/interface issues in .NETVerina
Glad to help! As an addendum, you might consider explicitly implementing the interface on CustomizableControl so that those members are only "visible" when the control is actually being used in its customizable capacity.Diplegia
I'll also add that this solution can be implemented cleanly - a quality that I believe has become rare in certain more modern .NET mentalities.Verina
Just to rain on the parade slightly, there are a couple of deficiencies with this compared to full MI as it could be in .NET. Firstly, you can't evolve an interface without breaking binary compatibility (generally not an issue for libraries with a small community of users). This may be fixed in some future version of the CLR, where they may allow interface methods to have a default implementation. Secondly, in MI each of the multiple base classes can add data fields to the combined class. You'd have to simulate that via extension methods with some kind of thread-local static dictionary mess.Spheroid
@Earwicker: You raise some excellent points. Your comment is right on the money, considering this question asks about general alternatives to MI in .NET. This solution has great potential in certain instances, and I'm biased in its favor because it solved my particular problem so well. But while we're discussing this, I have to say that I am really disappointed with Microsoft here. The framework supports full OO, and they clearly don't care about minor performance hits. So why can't we MI where it makes sense? System.Object always makes a diamond-shape, but virtual inheritance solves that.Verina
Some great points. Extension methods really break down if you require shared state that isn't provided by the interface, so plan ahead! For more on default implementations of interfaces (and more), check out this video on Channel 9: Expert to Expert: Erik Meijer and Anders Hejlsberg - The Future of C# channel9.msdn.com/shows/Going+Deep/…Diplegia
A
29

You have two options here; use interfaces, or use composition. Honestly, interfaces are very powerful, and after reading this line

The interface solution doesn't make any sense to me. (Interfaces have never really made much sense to me, to be honest...)

I think that you should learn how to use them properly. That said, if there is simply some logic that multiple class need, but it does not make sense for these classes to inherit from the same base class, just create a class to encapsulate that logic and add a member variable of that class to your classes that are giving you problems. This way all of the classes contain the logic but can be separate in their inheritance hierarchies. If the classes should implement a common interface(s), then use interfaces.

Aluminum answered 17/8, 2009 at 18:52 Comment(1)
I really like the way you describe this. It seems I'll probably be taking the "composition" route. It'll complicate things annoyingly, but less so than having my implementations all over the place. (Haha, I'm so stubborn... I work up the courage to ask SO directly for help with interfaces, and I STILL can't swallow my pride and accept all the advice about the power of interfaces.)Verina
D
19

One approach is to use extension methods with an interface to provide your "derived class" implementation, much like 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"; } }
}

Usage: As long as you have a using directive for (or are in the same namespace as) the namespace where your extension methods are defined:

var cc = new CustomizableControl();
var header = cc.GetXamlHeader();
Diplegia answered 17/8, 2009 at 19:0 Comment(8)
Added a usage example for you. As long as the compiler can find them, extension methods look and feel just like instance methods. No extension properties, unfortunately, thus my renaming the method to follow method naming conventions.Diplegia
Also, check out this article on MSDN: msdn.microsoft.com/en-us/vcsharp/bb625996.aspxDiplegia
This solution solves the problem perfectly!! That is really freakin' cool. The bonus here is that I get to keep the implementation in a single location, in my object tree - instead of spreading it around polymorphically, or with interfaces - so it works great with the WPF control mentality. I recommend this solution to anyone who is having multiple-inheritance/interface issues in .NETVerina
Glad to help! As an addendum, you might consider explicitly implementing the interface on CustomizableControl so that those members are only "visible" when the control is actually being used in its customizable capacity.Diplegia
I'll also add that this solution can be implemented cleanly - a quality that I believe has become rare in certain more modern .NET mentalities.Verina
Just to rain on the parade slightly, there are a couple of deficiencies with this compared to full MI as it could be in .NET. Firstly, you can't evolve an interface without breaking binary compatibility (generally not an issue for libraries with a small community of users). This may be fixed in some future version of the CLR, where they may allow interface methods to have a default implementation. Secondly, in MI each of the multiple base classes can add data fields to the combined class. You'd have to simulate that via extension methods with some kind of thread-local static dictionary mess.Spheroid
@Earwicker: You raise some excellent points. Your comment is right on the money, considering this question asks about general alternatives to MI in .NET. This solution has great potential in certain instances, and I'm biased in its favor because it solved my particular problem so well. But while we're discussing this, I have to say that I am really disappointed with Microsoft here. The framework supports full OO, and they clearly don't care about minor performance hits. So why can't we MI where it makes sense? System.Object always makes a diamond-shape, but virtual inheritance solves that.Verina
Some great points. Extension methods really break down if you require shared state that isn't provided by the interface, so plan ahead! For more on default implementations of interfaces (and more), check out this video on Channel 9: Expert to Expert: Erik Meijer and Anders Hejlsberg - The Future of C# channel9.msdn.com/shows/Going+Deep/…Diplegia
S
7

I'm looking at this, and CustomizableObject is just screaming to be made into an interface (and since every concrete type is convertable to object, that part of the name is redundant). The problem you're running into is that you're not sure how to preserve some basic logic that will be shared or only vary slightly by implementation, and you want to store this logic in the tree itself so that it will work polymorphically (is that a word?).

You can achieve this through delegates. I'm not sure exactly which members are giving you trouble, but perhaps something more like this:

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

Additionally, you likely have some logic that is genuinely static that you feel really belongs with your CustomizableObject type. But this is probably false: you built the type with the intent of using that type in a specific situation. For example, from the context it looks like you'll create these controls and animations and use them on a Windows Form. The thing to do is have your own form that inherits from the base System.Windows.Form, and this new form type should know about ICustomizableObject and how to use it. That's where your static logic will go.

This seems a little awkward, but it's proven accurate when you decide to change presentation engines. What happens if you port this code to WPF or Silverlight? They'll likely need to use your implementation code a little differently than Windows Forms would, and you'll still likely need to change your CustomizableControl implementations. But your static logic is now all in exactly the right place.

Finally, the LoadObject() method you're using stands out to me as in the wrong place as well. You're saying you that you want every Customizable type to provide a method you can call that knows how to load/construct itself. But this is really something different. You might want yet another interface named IConstructable<T> and have your ICustomizable type implement that (IConstructable<ICustomizable>).

Spotless answered 17/8, 2009 at 19:3 Comment(2)
+1 Hmmmm ... you raise a very interesting point. I would indeed like for my sprite controls to be self-contained. This is WPF after all, so I probably shouldn't just inherit from the base window type like we did in WinForms - but I could easily apply this solution to, say, my SpriteLayer or SpriteView controls.Verina
I have to say, I really like the delegate approach as well. I actually spent some time coding, trying to see how delegates would work for this. I was planning on accepting your answer, once I got it to compile and run - extension methods just worked out better, and cleaner I think. However, this is definitely a great solution, worthy of notice for sure. In fact, I'm willing to bet there are plenty of instances where delegates would solve the problem even better than extension methods.Verina
K
2

Looks like you'll have to resort to interfaces and object composition.

Koerlin answered 17/8, 2009 at 18:52 Comment(0)
F
2

I use mixins in such a case. mixins are a powerful concept used in many languages to add functionality to a class at runtime. mixins are well known in many languages. The re-mix framework is a framework that brings the mixin technology to .NET.

The idea is simple: Decorate your class with a class attribute that represents a mixin. At runtime this functionality will be added to your class. So you can emulate multiple inheritance.

The solution to your problem could be to make CustomizableObject to a mixin. You will need the re-mix framework for this.

[Uses(CustomizableObjectMixin)] CustomizableControl : UserControl

Please check out remix.codeplex.com for more details.

Forras answered 24/3, 2011 at 8:42 Comment(0)
T
2

I think is a one of solution.

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()
    {

    }
}

Class MultipleInheritance add new behavior to two different object without affecting the behavior of this objects. MultipleInheritance have the same behaviors as delivered objects (Inherits both behaviors).

Tristis answered 3/4, 2013 at 21:39 Comment(2)
I don't see that as a decorator pattern.Nyssa
I think this is a valid and possible approach in some contexts, I don't see why you were downvoted. Obviously, it's not a general approach since decorators are meant to extend existing classes. Although you could use the decorator type directly and add some completely new methods to it. I can see that as not being the decorator pattern, but it can work as a variation of composition.Fotinas

© 2022 - 2024 — McMap. All rights reserved.