泛型还是非泛型

6

基本上,我有一个自定义的列表类,其中包含不同的水果。假设每个水果都有一个存储在列表中的ID号码。

是选择以下哪种方式更好:

new AppleList();
new OrangeList();
new LemonList();

或者

new FruitList<Fruit.Apple>();
new FruitList<Fruit.Orange>();
new FruitList<Fruit.Lemon>();

需要考虑的事项:

  • 所有ID都是int类型。
  • 水果的类型不会影响列表本身的实现。它只会被列表的客户端(如外部方法等)使用。

我想使用更清晰、设计更好、更快、更高效的一种。此外,如果上述两种技术都不是最好的,请提出您的想法。

编辑:顺便说一句,Fruit是一个枚举类型,如果这不清楚的话。


谢谢,不是枚举列表。它只是用于类型。所有这些列表将存储整数,如1、2、3、4。 - Joan Venge
1
我真的很惊讶有多少人不理解你正在存储整数列表。 - Jonathan Allen
我认为问题的一部分是你的问题过于抽象。也许你应该用实际情况来描述它? - Erik Funkenbusch
我应该澄清一下,我并不是在说你的问题愚蠢,只是那些类比有点不恰当 ;) - Erik Funkenbusch
有点道理,但正如我所说,我只是想把问题的重点放在最重要的地方。在实际情况中,额外的细节会使主要问题失去焦点。 - Joan Venge
显示剩余4条评论
8个回答

10
使用组合框:
public class AppleList : FruitList<Apple> { ... }
public class OrangeList : FruitList<Orange> { ... }
public class LemonList : FruitList<Lemon> { ... }

将通用逻辑放在基本列表类中:

public class FruitList<T> : List<T>
    where T : IFruit 
{ ... }

谢谢,我认为这不会起作用。因为所有的水果列表都将是100%相同的。但我仍然需要一种外部区分它们的方法。 - Joan Venge
1
如果它们都是相同的,那么你可以只使用AppleList: List<Apple>。 - John Sheehan
谢谢。在你的情况下,Apple是一个类吗?还有为什么要继承List?只是好奇。 - Joan Venge
因为您可以在其上构建的列表的所有功能都可以获得。 - John Sheehan

9
如果您使用泛型,创建FruitList类型有什么目的呢?您可以直接使用List吗?
性能上不会有太大的差异,所以我认为为什么要创建三个不同的类,而一个类就可以做到完全相同的事情呢?使用泛型解决方案。

像什么?你的功能不能通过普通列表上的扩展方法来实现吗?你所说的“必须实现新接口”是什么意思? - Erik Funkenbusch
我不建议在控制权归属于某个类的情况下使用扩展方法。 - gcores
@gcores:您不会建议在我们无法控制的类上使用扩展方法吗?只是好奇。 - Joan Venge
Joan,你能举个例子吗? - Erik Funkenbusch
要注意的是,继承和多态性是好事情,但如果过度使用,它会创建一个巨大的耦合纠缠,使您的代码在未来更难修改(这是具有讽刺意味的,因为如果做得好,它会产生相反的影响)。 - Michael Meadows
显示剩余12条评论

7

维护一个通用列表比维护三个非通用版本要容易得多。如果你真的喜欢 AppleList 这个名称,你可以使用 using 技巧来命名一个通用列表。

using AppleList=Fruit.FruitList<Fruit.Apple>

如果他真的需要AppleList,那么使用“技巧”让它看起来像AppleList并不意味着它就是一个AppleList。 - Max Schmeling
我不知道这种语法是这样使用的。所以当我说AppleList时,它会创建FruitList<Fruit.Apple>吗? - Joan Venge
@Max,我认为你误解了问题。OP正在询问他们是否应该创建3种列表类型还是只创建一个通用列表。我的解决方案允许为通用列表设置“友好”名称。原则上,它们应该具有相同的所有方法。 - JaredPar

3

如果您要添加额外的功能,请重用通用集合类并仅对它们进行子类化。 如果可能的话,请保持您的子类实现的通用性。这是最简单的实现方式。


3

•所有的ID都是int类型。

•水果的类型不会影响列表本身的实现。它只会被列表的客户端使用,例如外部方法等。

考虑到这两个事实,我不会费心使用泛型。我会在FruitList上放置一个普通属性,以指示它是哪种类型的水果。


为什么他要维护三个做同样事情的不同列表?这里唯一的区别就是包含了不同类型水果的 id,但它并不影响列表的行为。为什么不直接使用 var appleList = new List<int>()... - Beachwalker

1
我不建议采纳这个答案,我认为您可能是想要这样的:
public enum Fruit
{
   Apple,
   Orange,
   Lemon
}

public interface IFruitList : IList<int>
{
   Fruit Type { get; }
};

public class FruitList : List<int>, IFruitList
{
   private readonly type;

   FruitList(Fruit type)
     : base()
   {
      this.type = type;
   }

   FruitList(Fruit type, IEnumerable<int> collection)
     : base(collection)
   {
      this.type = type;
   }

   Fruit Type { return type; }
}

1
使用通用列表,没有必要创建3个列表,保持一定的抽象级别总是很好的选择。(IFruit将是一个不错的接口)。

IFruit 有什么好处?它不适用于存储在列表中的整数。 - Jonathan Allen
为什么不直接使用 var appleList = new List<int>();,容器可以在外部定义,你可以创建一个类 FruitList : List<int> { Fruit Type { get; }}。 - Beachwalker

0

在编程中,你应该假设 YAGNI,除非你需要它。因此,如果你不需要比 List 更多的东西,那么只需使用 List<T>。如果出于某种原因你必须覆盖 List,那么创建

FruitList<T> : List<T> where T : Fruit

如果您的列表分歧并且不再是多态的,请考虑实现自定义列表:
  • AppleList
  • OrangeList
  • LemonList
尽可能保持您的继承层次结构尽可能平坦,以避免过度复杂化您的解决方案。

你没有听清楚。这个列表包含整数,而不是水果。 - Jonathan Allen
其实我不知道这个:List不是一个密封类吗? - Joan Venge
List<T> 绝对不是密封的。 - Michael Meadows
@Grauenwolf Joan 表示 ID 是一个 int 类型。如果 fruit 是一个 int 类型,那么就不可能执行这个操作:new FruitList<Fruit.Apple>(); 这在例子中已经包含了。Apple 必须是 Fruit 的嵌套类型...但还是感谢你的建设性批评。 - Michael Meadows
不,这个列表包含水果。也许只有FruitList:List<T>就足够了,而FruitList<T>并不是必需的,但答案仍然是正确的。 - gcores
谢谢大家。实际上,我在水果类中并没有使用继承,而是使用了枚举。在这种情况下,这不是更好的方式吗?我想,如果水果的类型只对其他方法很重要,为什么要创建新的类呢? - Joan Venge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接