.NET中的多重继承有哪些好的替代方案?

时间:2009-08-17 18:47:24

标签: c# .net .net-3.5 inheritance multiple-inheritance

我在WPF应用程序中遇到了类层次结构的问题。这是你有两个继承树合并在一起的问题之一,你找不到任何逻辑方法来使你的继承顺利工作没有多重继承。我想知道是否有人有任何明智的想法让这种系统工作,而不会让它无法遵循或调试。

我是一名低级工程师,所以我的第一个想法总是,“哦!我只是用原生C ++写一些这些类,并在外部引用它们!然后我可以拥有我所有的老式OO有趣!”唉,当你需要从托管控件继承时,这没有用......

请允许我显示当前投影类图的片段:

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

问题非常明确:CustomizableObject和CustomizableControl对象树的分离---以及将UserControl插入树中的一个,但不是两个。

将CustomizableObject的实现移动到其派生类中没有任何实际意义,因为实现不会因类而异。此外,多次实施它会非常困惑。所以我真的不想让CustomizableObject成为一个接口。界面解决方案对我没有任何意义。 (接口有永远对我来说真的很有意义,说实话......)

所以我再说一遍,有人有什么好主意吗?这是一个真正的泡菜。我想了解更多关于使接口与我的对象树一起工作的信息,而不是反对它。我正在使用WPF和C#制作这个简单的精灵引擎作为一个可靠的练习,比什么都重要。这在C ++中很容易解决 - 但是我需要弄清楚如何在托管环境中解决这些问题,而不是随时随地将手伸向空中并运行回Win32。

6 个答案:

答案 0 :(得分:29)

这里有两个选择;使用接口,或使用组合。老实说,界面非常强大,读完这一行后

  

界面解决方案对我没有任何意义。 (说实话,接口从来没有真正对我有意义。)

我认为您应该学习如何正确使用它们。也就是说,如果只有一些逻辑表明多个类需要,但是这些类从同一个基类继承是没有意义的,只需创建一个类来封装该逻辑并将该类的成员变量添加到您的类中这给你带来了麻烦。这样,所有类都包含逻辑,但在它们的继承层次结构中可以是独立的。如果类应该实现公共接口,那么使用接口。

答案 1 :(得分:17)

一种方法是使用带接口的扩展方法来提供“派生类”实现,就像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();

答案 2 :(得分:7)

我正在看这个,CustomizableObject只是尖叫成一个接口(并且由于每个具体类型都可以转换为对象,因此该部分名称是多余的)。您遇到的问题是您不确定如何保留一些将被共享的基本逻辑或仅因实现而略有不同,并且您希望将此逻辑存储在树本身中以便它将以多态方式工作(是一个字?)。

您可以通过代表来实现这一目标。我不确定哪些成员给你带来麻烦,但也许更像是这样:

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

此外,你可能有一些真正静态的逻辑,你觉得你的CustomizableObject类型确实属于。但这可能是错误的:您构建了类型,意图在特定情况下使用该类型。例如,从上下文看起来您将创建这些控件和动画并在Windows窗体上使用它们。要做的是拥有自己的表单,继承自System.Windows.Form基础,这个新的表单类型应该知道ICustomizableObject以及如何使用它。这就是静态逻辑的用武之地。

这看起来有点尴尬,但是当您决定更改演示引擎时,它已被证明是准确的。如果将此代码移植到WPF或Silverlight会发生什么?他们可能需要使用您的实现代码,而不是Windows Forms,您仍然可能需要更改CustomizableControl实现。但是你的静态逻辑现在都处于正确的位置。

最后,你使用的LoadObject()方法在我错误的地方也很突出。你说你希望每个Customizable类型都提供一个你可以调用的方法,它知道如何加载/构建自己。但这确实是不同的。您可能还需要另一个名为IConstructable<T>的接口,并使您的ICustomizable类型实现(IConstructable<ICustomizable>)。

答案 3 :(得分:2)

在这种情况下我使用mixins。 mixins是一种强大的概念,在许多语言中用于在运行时向类添加功能。 mixins以多种语言而闻名。重组混合框架是一个将mixin技术引入.NET的框架。

这个想法很简单:使用代表mixin的类属性来装饰你的类。在运行时,此功能将添加到您的类中。因此,您可以模拟多重继承。

您的问题的解决方案可能是将CustomizableObject设置为mixin。你需要重新混合框架。

[用途(CustomizableObjectMixin)] CustomizableControl:UserControl

请查看remix.codeplex.com了解更多详情。

答案 4 :(得分:1)

看起来你将不得不求助于接口和对象组合。

答案 5 :(得分:1)

我认为这是一个解决方案。

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将新行为添加到两个不同的对象,而不会影响此对象的行为。 MultipleInheritance与传递的对象具有相同的行为(继承这两种行为)。