C# 具有受约束子类型的通用容器,具有可重用的基本实现
这是从邮局开始的 这应该是UI API的一部分,但我已经删除了本例中的选项卡和页面内容,因为这实际上只是一个结构问题。抱歉这么长,我已经尽量缩短了 也就是说,我知道问题出在哪里。我只是不确定是否有更清晰的方法来实现这个结构,从而避免这些错误传递给API用户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&
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; } }
}