具有受约束子类型的通用容器,具有可重用的基本实现

时间:2015-01-16 10:10:11

标签: c# .net generics

这是this发布的。

这应该是UI API的一部分,但是我已经从这个例子中删除了标签和页面内容,因为它实际上只是一个结构性问题。道歉,我尽可能地减少了它。

还要明确我知道问题是什么。我只是不确定是否有更清晰的方法来实现这种结构,避免将这些错误传递给API用户。

public abstract class Container<TSub, TSub2>
    where TSub : Sub<TSub, TSub2>, new()
    where TSub2 : Sub2<TSub, TSub2>, new()
{
    private TSub sub;

    public Container()
    {
        this.sub = new TSub();
        this.sub.Container = this;
    }
}

public abstract class Sub<TSub, TSub2>
    where TSub : Sub<TSub, TSub2>, new()
    where TSub2 : Sub2<TSub, TSub2>, new()
{
    private Container<TSub, TSub2> container;
    private TSub2 sub2;

    public Sub()
    {
        this.sub2 = new TSub2();
    }

    public Container<TSub, TSub2> Container
    {
        get { return this.container; }
        internal protected set
        {
            this.container = value;
            this.sub2.Container = value;
        }
    }
}

public abstract class Sub2<TSub, TSub2>
    where TSub : Sub<TSub, TSub2>, new()
    where TSub2 : Sub2<TSub, TSub2>, new()
{
    public Sub2() { }

    public Container<TSub, TSub2> Container { get; internal protected set; }
}

public class DefaultContainer : Container<DefaultSub, DefaultSub2> { }

public class DefaultSub : Sub<DefaultSub, DefaultSub2> { }

public class DefaultSub2 : Sub2<DefaultSub, DefaultSub2> { }

到目前为止,所有这些都与预期完全一致。您可以制作新的DefaultContainer个实例,并获取嵌套在下面的强类型DefaultSubDefaultSub2部分。

你也可以制作一个扩展的替代容器,sub和sub2,它也可以正常工作。

我现在遇到的问题在于:

public class AlternateContainer : Container<AlternateSub, DefaultSub2> { }

public class AlternateSub : Sub<AlternateSub, DefaultSub2> { }

问题显而易见 - AlternateContainerAlternateSub<AlternateSub, DefaultSub2>)签名的泛型部分与DefaultSub2(<DefaultSub, DefaultSub2>的签名不匹配)。

但是,虽然API用户经常覆盖所有3个类,但他们有时不需要覆盖其中的1个或2个,而只是想使用其他类的默认实现。 / p>

显然有两个相当基本的解决方案 - 一个是API用户只需要包含一个使用正确泛型扩展默认实现的空白类,另一个是使用静态类提供大部分默认实现并提供样板。< / p>

我从我的测试用户那里找到的是他们中的大多数人正在编写上面的代码,然后得到其中几个编译器错误

Error   1   The type 'ClassLibrary1.DefaultSub2' cannot be used as type parameter 'TSub2' in the generic type or method 'ClassLibrary1.Container<TSub,TSub2>'. There is no implicit reference conversion from 'ClassLibrary1.DefaultSub2' to 'ClassLibrary1.Sub2<ClassLibrary1.AlternateSub,ClassLibrary1.DefaultSub2>'.

然后他们花时间在试图找出推荐修复的文档中挖掘。

有没有人对如何提供这些通用子类有任何建议,但是可以重复使用基础实现吗?


更新

此模式的主要用途是用于自定义选项卡控件。这为核心功能提供了基本实现,包括鼠标上下事件处理基本(可过度删除)皮肤等。使用这些类的开发人员完全不受我的控制,所以我不知道所有内容的全部内容他们可能想要这些。很明显,许多人想要覆盖警报或状态图标,以及可以轻松更新的数字或字符串。

主要标签(Container)当然可以非常轻松地提供标签的强类型版本(Sub)和标签页(Sub2)。但是,也要求访问以下属性:

Page.Tab
Tab.Page
    /* Must return the specific generic type for access to custom properties */

当然,如果删除了通用约束,这些很容易,但是在BASE实现中需要这些约束。最后,还需要这些属性:

Page.TabContainer
Tab.TabContainer
    /* This instance must include the <TTab, TPage> constraint */

烦人的事情当然是,我有这一切工作!但是,就目前而言,如果你想创建一个带有默认选项卡和页面的自定义容器,你必须提供一个样板类来完成它,而且它不是很清楚。

此外,它只是感觉不对。我只是有这种琐碎的感觉,有一种更简单,更强烈的表达方式。

感谢您阅读所有这些!还有什么想法吗?


更新2

我有另一种可能的方法来避免传递泛型,但它确实缺少所需的功能:

using System;
using System.Collections.Generic;
using System.Linq;

public abstract class Container2<TTab, TPage>
    where TTab : Tab, new()
    where TPage : TabPage, new()
{
    private Dictionary<int, TTab> index;
    private Dictionary<TTab, TPage> tabs;

    public Container2()
    {
        this.index = new Dictionary<int, TTab>();
        this.tabs = new Dictionary<TTab, TPage>();
    }

    public TTab this[int index] { get { return this.GetTab(index); } }

    public void AddTab(string text)
    {
        int index = this.tabs.Count;
        TTab tab = new TTab();
        TPage page = new TPage();

        tab.Text = text;
        tab.Page = page;

        this.tabs.Add(tab, page);
        this.index.Add(index, tab);
    }

    public TPage GetPage(int index) { return this.tabs[this.index[index]]; }

    public TPage GetPage(TTab tab) { return this.tabs[tab]; }

    public TTab GetTab(int index) { return this.index[index]; }

    public TTab GetTab(TPage page) { return this.tabs.FirstOrDefault(x => x.Value == page).Key; }
}

public abstract class Tab
{
    private TabPage page;
    private string text;

    public Tab()
    {
    }

    public TabPage Page { get { return this.page; } internal set { this.page = value; } }

    public string Text { get { return this.text; } set { this.text = value; } }
}

public abstract class TabPage
{
    private Tab tab;

    public TabPage()
    {
    }

    public Tab Tab { get { return this.tab; } }
}

仅将标签链接到页面,反之亦然。如果您需要扩展页面或选项卡类型,则必须通过容器。一切正常,但是无法保持从选项卡或页面到容器的链接,这意味着您必须将引用传递给about。

所以现在我要么能够保存容器的链接,也没有理由使用它,或者没有链接回容器,以及使用它的绝对要求。

我想我已经准备好以这种方式打电话了,但我只想尝试为此选择“最佳”模式。一旦它投入生产,它几乎不可能被替换。

最后的想法?任何人吗?

0 个答案:

没有答案