Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/269.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有受约束子类型的通用容器,具有可重用的基本实现_C#_.net_Generics - Fatal编程技术网

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

C# 具有受约束子类型的通用容器,具有可重用的基本实现,c#,.net,generics,C#,.net,Generics,这是从邮局开始的 这应该是UI API的一部分,但我已经删除了本例中的选项卡和页面内容,因为这实际上只是一个结构问题。抱歉这么长,我已经尽量缩短了 也就是说,我知道问题出在哪里。我只是不确定是否有更清晰的方法来实现这个结构,从而避免这些错误传递给API用户 public abstract class Container<TSub, TSub2> where TSub : Sub<TSub, TSub2>, new() where TSub2 : Sub2&

这是从邮局开始的

这应该是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> { }
这里的问题很明显-
AlternateContainer
AlternateSub
)的签名的泛型部分与DefaultSub2的签名不匹配(

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

显然有两个相当基本的解决方案——一个是让API用户简单地包含一个空白类,用正确的泛型扩展默认实现,另一个是用静态类提供大部分默认实现并提供样板文件

不过,我从测试用户那里发现,他们中的大多数人都是按照上面的方式编写代码,然后出现了一些编译器错误

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>'.
当然,如果删除了泛型约束,这些就足够容易了,但是在基本实现中需要这些约束。最后,这些属性也是必需的:

Page.TabContainer
Tab.TabContainer
    /* This instance must include the <TTab, TPage> constraint */
Page.TabContainer
Tab.TabContainer
/*此实例必须包含约束*/
恼人的是,当然,我有所有这些工作!!然而,目前的情况是,如果您想使用默认选项卡和页面创建一个定制容器,那么必须提供一个样板类来实现这一点,这还不是很清楚

而且,它确实感觉不对。我只是有一种烦琐的感觉,有一种更简单、更有力的方式来表达这类事情

谢谢你阅读这一切!还有其他想法吗


更新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; } }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
公共抽象类容器2
其中TTab:Tab,new()
其中TPage:TabPage,new()
{
专用词典索引;
专用词典标签;
公共集装箱2()
{
this.index=新字典();
this.tabs=新字典();
}
公共TTab this[int index]{get{返回this.GetTab(index);}
公共void AddTab(字符串文本)
{
int index=this.tabs.Count;
TTab选项卡=新的TTab();
t页面=新的t页面();
tab.Text=文本;
tab.Page=Page;
此.tabs.Add(选项卡,页面);
this.index.Add(索引,选项卡);
}
公共TPage GetPage(int index){返回this.tabs[this.index[index]];}
公共TpageGetPage(TTab选项卡){返回this.tabs[tab];}
公共TTab GetTab(int-index){返回this.index[index];}
public TTab GetTab(TPage page){返回this.tabs.FirstOrDefault(x=>x.Value==page.Key;}
}
公共抽象类选项卡
{
专用标签页;
私有字符串文本;
公共选项卡()
{
}
公共选项卡页面{get{return this.Page;}内部集合{this.Page=value;}
公共字符串Text{get{返回this.Text;}set{this.Text=value;}}
}
公共抽象类选项卡页
{
私人标签;
公共选项卡页()
{
}
公共选项卡{get{返回this.Tab;}}
}
只在基本实现中将选项卡链接到页面,反之亦然。如果需要扩展页面或选项卡类型,则必须遍历容器。这一切正常,但是没有办法维护从选项卡或页面到容器的链接,这意味着您必须将引用传递给这两个容器

所以现在我要么有能力持有一个指向容器的链接,并且没有理由使用它,要么没有指向容器的链接,以及使用它的绝对要求

我想我几乎已经准备好把这称为一种方式或另一种方式,但我只是想尝试为这选择“最佳”模式。一旦投入生产,它几乎不可能被取代


最后有什么想法吗?有人吗?

一旦您通过使
DefaultSub2
继承
Sub2
关闭泛型类型,您的抽象类中的TSub和TSub2的类型将被设置。除非DefaultSub2是一个开放的泛型,否则没有一种方法可以让类从DefaultSub2继承,从而将抽象类中变量的类型更改为您想要的类型。是的,我知道问题是什么,这对于为什么不起作用是有意义的。但是,我不能将这些类作为开放泛型,还有很多基本功能需要实现。理想情况下,我希望提供一个使用
DefaultSub2
容器
类,但我当然遇到了同样的问题。正如我所说,我理解这个问题,但从API的角度来看,目前这种结构似乎过于混乱,无法使用。老实说,看到这种自引用的通用实现触发了我的代码嗅觉传感器。这些通用参数只是为了保留元数据吗?也许这可以简单地用一个更好的模式进行修改,以避免泛型问题。我完全同意,必须到处传递约束类型参数感觉是错误的,但是无论我尝试什么其他模式,我要么最终得到类型gotcha,要么必须到处进行大量类型转换。可能删除模式的示例用例在这里并没有完全帮助-我将更新这个问题。API需要强类型有什么好的理由吗?无论如何,您将无法使用不同类型的选项卡强键入选项卡控件
Page.TabContainer
Tab.TabContainer
    /* This instance must include the <TTab, TPage> constraint */
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; } }
}