困惑的东西看起来像奇怪的递归模板模式

时间:2017-07-25 14:14:25

标签: c# templates generics inheritance crtp

我有一个由几个"级别组成的应用程序"元素。有些元素是0..N子元素的父元素,它们本身是0..N其他元素的父元素。顶级类型没有父级,底级元素没有子级。

换句话说,设三种类型:A,B和C. A的实例是B的多个实例的父实例,它们本身是C的多个实例的父实例。每个实例也有一个(强类型)引用到它的父母。

我在父类中有几个相同的方法,比如AddChild,RemoveChild,GetChildIndex等。我想为所有父类创建一个基类,以便不为每个父类复制这些方法。

这个想法是,当从父基类派生时,你必须根据父类的类型提供子类的类型。

到目前为止,我已经提出了这种过于复杂的设计:

public interface IChild<TParent> where TParent : ParentBase<IChild<TParent>>
{
    TParent Parent { get; set; }
}

public class ParentBase<TChild> where TChild : IChild<ParentBase<TChild>>
{
    public List<TChild> Children;
}

public class A : ParentBase<B>
{
}

public class B : ParentBase<C>, IChild<A>
{
}

public class C : IChild<B>
{
}

但是我收到了编译错误:

Error   CS0311  The type 'TemplateTest.IChild<TParent>' cannot be used as type parameter 'TChild' in the generic type or method 'ParentBase<TChild>'. There is no implicit reference conversion from 'TemplateTest.IChild<TParent>' to 'TemplateTest.IChild<TemplateTest.ParentBase<TemplateTest.IChild<TParent>>>'.    Kbd2    C:\dev\Kbd2\TemplateTest.cs 6   Active
Error   CS0311  The type 'TemplateTest.ParentBase<TChild>' cannot be used as type parameter 'TParent' in the generic type or method 'IChild<TParent>'. There is no implicit reference conversion from 'TemplateTest.ParentBase<TChild>' to 'TemplateTest.ParentBase<TemplateTest.IChild<TemplateTest.ParentBase<TChild>>>'. Kbd2    C:\dev\Kbd2\TemplateTest.cs 11  Active
Error   CS0311  The type 'TemplateTest.B' cannot be used as type parameter 'TChild' in the generic type or method 'ParentBase<TChild>'. There is no implicit reference conversion from 'TemplateTest.B' to 'TemplateTest.IChild<TemplateTest.ParentBase<TemplateTest.B>>'.  Kbd2    C:\dev\Kbd2\TemplateTest.cs 16  Active
Error   CS0311  The type 'TemplateTest.C' cannot be used as type parameter 'TChild' in the generic type or method 'ParentBase<TChild>'. There is no implicit reference conversion from 'TemplateTest.C' to 'TemplateTest.IChild<TemplateTest.ParentBase<TemplateTest.C>>'.  Kbd2    C:\dev\Kbd2\TemplateTest.cs 20  Active
Error   CS1721  Class 'B' cannot have multiple base classes: 'ParentBase<C>' and 'IChild<A>'    Kbd2    C:\dev\Kbd2\TemplateTest.cs 20  Active
Error   CS0311  The type 'TemplateTest.B' cannot be used as type parameter 'TParent' in the generic type or method 'IChild<TParent>'. There is no implicit reference conversion from 'TemplateTest.B' to 'TemplateTest.ParentBase<TemplateTest.IChild<TemplateTest.B>>'.    Kbd2    C:\dev\Kbd2\TemplateTest.cs 24  Active

我甚至不确定这是否可以编译,因为类相互依赖。难道我做错了什么 ?谢谢。

编辑 :添加了未实现的方法和更新的错误列表。

编辑 :通过使子接口成为基类而不是接口来简化示例,就像父类一样。

编辑 :实际上只允许一个基类,所以我将子类型转换回接口而不是类。

编辑 :如果我删除其中任何一个&#34;其中&#34;约束,错误都消失了。是因为他们互相依赖吗?

1 个答案:

答案 0 :(得分:0)

您当前的方法存在的问题是它依赖于某种奇怪的递归。这就像说:

  • 孩子是有父母的人
  • 且该父母是有孩子的父母
  • 的人
  • 该孩子是父母的孩子,其子女有父母
  • ......这不起作用!

如果您再考虑一下,父/子关系涉及两种类型:

public interface IChild<P, C> where P : IParent<P, C> where C : IChild<P, C> {
    P Parent { get; set; }
}

public interface IParent<P, C> where P : IParent<P, C> where C : IChild<P, C> {
    IList<C> Children { get; }
}

虽然这可能有效但看起来非常复杂,因为在IParent中未使用类型参数P,而在IChild中未使用类型参数C

但我认为我们正走在正确的轨道上。如果您简化接口并将通用约束添加到ParentBase,您将获得更易理解的内容:

public interface IChild<P> {
    P Parent { get; set; }
}

public interface IParent<C> {
    IList<C> Children { get; }
}

public class ParentBase<P, C> : IParent<C> where C : IChild<P>  {
    public IList<C> Children { get; } = new List<C>();
}

使用此设计,您有separated concerns

  • 父母的接口
  • 儿童界面
  • 和基类实现

您的示例有效:

public class A : ParentBase<A, B> {
}

public class B : ParentBase<B, C>, IChild<A> {
    public A Parent { get; set; }
}

public class C : IChild<B> {
    public B Parent { get; set; }
}

public class Demo {
    public static void Test() {
        var a = new A();
        var b = new B() { Parent = a };
        var c = new C() { Parent = b };
        a.Children.Add(b);
        b.Children.Add(c);
        System.Console.WriteLine(c.Parent.Parent == a);
    }
}

还有一些其他细节需要注意:

  • 对于已发布的列表,通常使用接口IList<T>从实现中抽象出来
  • 公共字段(如children中的ParentBase)并不常见,大多数人使用只读属性来封装列表本身