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

发布于 2024-08-02 07:01:23 字数 2143 浏览 2 评论 0原文

我在 WPF 应用程序中的类层次结构中遇到了一些问题。这是其中一个问题,其中两个继承树合并在一起,并且您无法找到任何逻辑方法来使您的继承在没有多重继承的情况下顺利工作。我想知道是否有人有任何好主意来让这种系统正常工作,而又不会使其无法遵循或调试。

我是一名低级工程师,所以我的第一个想法总是,“哦!我只需用本机 C++ 编写其中一些类,并在外部引用它们!然后我就可以享受所有老式 OO 的乐趣了!”唉,当您需要从托管控件继承时,这没有帮助...

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

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

问题非常清楚:CustomizedObject 和 CustomizedControl 对象树的分离 --- 和将 UserControl 插入一棵树中,但不是两棵树中。

将 CustomizedObject 的实现移至其派生类中没有实际意义,因为实现不会因类而异。此外,多次实施确实会造成混乱。所以我真的不想让CustomizedObject成为一个接口。界面解决方案对我来说没有任何意义。 (老实说,界面从来对我来说从来没有真正有意义......)

所以我再说一遍,有人有什么好主意吗?这是真正的泡菜。我想了解更多关于如何使接口与我的对象树一起工作,而不是反对它。我正在使用 WPF 和 C# 制作这个简单的精灵引擎,作为一项扎实的练习,这比什么都重要。这在 C++ 中很容易解决 - 但我需要弄清楚如何在托管环境中解决这些问题,而不是每当事情变得困难时就举手投足并跑回 Win32。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

笑忘罢 2024-08-09 07:01:23

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

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

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

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.

作死小能手 2024-08-09 07:01:23

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

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();
最单纯的乌龟 2024-08-09 07:01:23

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

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

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

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

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

最后,您使用的 LoadObject() 方法对我来说也很突出,因为它也出现在错误的位置。您是说您希望每个可自定义类型都提供一个可以调用的方法,该方法知道如何加载/构造自身。但这确实是不同的东西。您可能还需要另一个名为 IConstructable 的接口,并让您的 ICustomizable 类型实现该接口 (IConstructable)。

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>).

带上头具痛哭 2024-08-09 07:01:23

看来您必须求助于接口和对象组合。

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

冷默言语 2024-08-09 07:01:23

在这种情况下我使用 mixin。 mixin 是一个强大的概念,在许多语言中使用,用于在运行时向类添加功能。 mixin 在许多语言中都是众所周知的。 re-mix框架是将mixin技术引入.NET的框架。

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

问题的解决方案可能是将 CustomizedObject 制作为 mixin。为此,您将需要重新混合框架。

[使用(可定制对象混合)]
CustomizedControl :用户控件

请查看 remix.codeplex.com 了解更多详细信息。

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.

画▽骨i 2024-08-09 07:01:23

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

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

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文