【问题标题】:Using generics in a Parent Child relationship在父子关系中使用泛型
【发布时间】:2010-08-03 01:38:16
【问题描述】:

我有一个抽象类 BaseItem 声明如下:

public abstract class BaseItem
{
    public BaseItem Parent { get; protected set; }
    public List<BaseItem> Children = new List<BaseItem>();

    public abstract string Function1();        
}

基本上,我正在尝试实现一种设计,其中每个项目都有一个特定类型的父项和不同类型的子项。

例如,ItemA 将具有所有 ItemB 类型的子项。然后 ItemB 将有一个 ItemA 类型的父项和所有 ItemC 类型的子项。 ItemC 将有 ItemB 的父项和 ItemD 类型的子项。

我认为使用泛型来避免不必要的强制转换会更简洁,因为我知道我的每个继承类的父类和子类将是什么类型。所以我想出了这样的事情:

public abstract class AbstractBase
{
    public abstract string Function1();
}

public abstract class BaseItem<T1, T2> : AbstractBase
    where T1 : AbstractBase
    where T2 : AbstractBase
{
    public T1 Parent { get; protected set; }
    public List<T2> Children = new List<T2>();
}

public class ItemA : BaseItem<ItemA, ItemB>
{
}
public class ItemB : BaseItem<ItemA, ItemC>
{
}
public class ItemC : BaseItem<ItemB, ItemD>
{
}
public class ItemD : BaseItem<ItemC, ItemD>
{
}

所以有两件事。 1. 这是一个好的设计吗?有没有更简单/更好的方法来做到这一点?我真的不喜欢仅仅为了能够使用泛型而使用第二个抽象基类。 2. 如果我保留这个,处理末端的最佳方法是什么? (即 ItemA 在我的实际问题域中没有父级,但它需要父级来编译。ItemD 没有子级,但我需要给它一些东西)

【问题讨论】:

  • 这个问题让我想用 FORTRAN。
  • 您的代码示例有public class ItemC : BaseItem&lt;ItemB, ItemC&gt; ,但您的意思可能是public class ItemC : BaseItem&lt;ItemB, ItemD&gt;

标签: c# generics


【解决方案1】:

如果我没听错的话,你是说ItemA 从来没有父母,ItemD 从来没有孩子,对吧?老实说,我可能只是用它们自己的正确类型的父/子属性声明单独的类:

public abstract class AbstractBase
{
    public abstract string Function1();
}

public class ItemA : AbstractBase
{
    public List<ItemB> Children = new List<ItemB>();
}
public class ItemB : AbstractBase
{
    public ItemA Parent { get; protected set; }
    public List<ItemC> Children = new List<ItemC>();
}
public class ItemC : AbstractBase
{
    public ItemB Parent { get; protected set; }
    public List<ItemD> Children = new List<ItemD>();
}
public class ItemD : AbstractBase
{
    public ItemC Parent { get; protected set; }
}

您在这里重复的唯一内容是 ParentChildren 属性/字段。您可以在AbstractBase 中实现的所有其他常见功能。 (当然,除非该功能需要访问父母/孩子 - 但即使在您的解决方案中,您也会回到原点。)

【讨论】:

  • 接受您的回答,因为最终,无论我采用哪种方式,让泛型发挥作用都是很大的开销,但收效甚微。
【解决方案2】:

我会这样做:

public interface IChildOf<T> {
    T Parent { get;  set; }
}

public interface IParent<T> {
    List<T> Children { get; set; }
}

//This class handles all of the cases that have both parent and children
public abstract class BaseItem<T1, T2> : IParent<T1>, IChildOf<T2>
{
    public List<T1> Children { get; set; }
    public T2 Parent { get; set; }
}

//This class handles the top level parent
public class ItemA : IParent<ItemB>
{
    public List<ItemB> Children { get;  set; }
}
public class ItemB : BaseItem<ItemC, ItemA>
{
}
public class ItemC : BaseItem<ItemD, ItemB>
{
}
//.... as many intermediates as you like.

//This class handles the bottom level items with no children
public class ItemD : IChildOf<ItemC>
{
    public ItemC Parent { get;  set; }
}

【讨论】:

    【解决方案3】:

    您可以使用两个通用接口,ChildOf&lt;T&gt;IList&lt;T&gt;。这将让您处理最终情况。不幸的是,由于 .NET 没有多重继承,您将无法共享实现。

    或者,您可以使用抽象类和标记类型(System.Void 是理想的,但我认为它不会编译)用于最终情况,并从Parent/Children 检查它属性并抛出异常。

    【讨论】:

    • 您始终可以使用 IoC/DI 来注入两个接口的默认实现
    【解决方案4】:

    这似乎不是一个太糟糕的想法,但老实说,我很想保留原来的解决方案并忍受铸造,因为它确实增加了相当多的复杂性而没有太多好处。 (尽管我必须承认我会尽可能用泛型替换强制转换)。

    如果您确实想保留它,有几个建议:

    • 将 AbstractBase 作为接口
    • 为末端做一些不同的事情(例如,两个额外的 BaseItem 采用父类和子类泛型)。

    显然,第二点只会增加复杂性,这是我不确定它是否值得的另一个原因。

    【讨论】:

      【解决方案5】:

      我认为以这种方式做事没有问题,因为您似乎有一个特定的层次结构,要求顶级项目的类型为 ItemA,二级项目的类型为 ItemB,等等

      为了解决根节点和叶节点,我可能会定义同样派生自AbstractBase 的单独类:

      public abstract class RootItem<T2> : AbstractBase
          where T2 : AbstractBase
      {
          public List<T2> Children = new List<T2>();
      }
      
      public abstract class LeafItem<T1> : AbstractBase
          where T1 : AbstractBase
      {
          public T1 Parent { get; protected set; }
      }
      
      public class ItemA : RootItem<ItemB>
      {
      }
      public class ItemB : BaseItem<ItemA, ItemC>
      {
      }
      public class ItemC : BaseItem<ItemB, ItemD>
      {
      }
      public class ItemD : LeafItem<ItemC>
      {
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-25
        • 2018-01-28
        • 1970-01-01
        • 2023-04-02
        • 2022-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多