简体   繁体   English

覆盖 IEnumerable<t> 在更多派生的 Class - 任何潜在问题?</t>

[英]Override IEnumerable<T> in More Derived Class - Any Potential Issues?

I have a abstract, custom collection type we'lll call AnimalCollection for storing a collection of Animal objects.我有一个抽象的自定义集合类型,我们将调用AnimalCollection来存储Animal对象的集合。 Animals that need to be stored in an animal collection are so special that they get their own implementation of AnimalCollection for storing additional information specific to that type of collection, eg TurtleCollection , GiraffeCollection , etc.需要存储在动物集合中的动物非常特殊,以至于它们拥有自己的AnimalCollection实现,用于存储特定于该类型集合的附加信息,例如TurtleCollectionGiraffeCollection等。

Since we're all collections here, I think it makes sense that AnimalCollection should implement IEnumerable<Animal> so users can iterate over it.由于我们在这里都是 collections,我认为AnimalCollection应该实现IEnumerable<Animal>以便用户可以迭代它是有道理的。 However, in the case of our more derived classes it makes more sense to have implemented IEnumerable<Turtle> , IEnumerable<Giraffe> , etc.然而,在我们更多派生类的情况下,实现IEnumerable<Turtle>IEnumerable<Giraffe>等更有意义。

class Animal {}
class Turtle : Animal {}

abstract class AnimalCollection : IEnumerable<Animal>
{
}

class TurtleCollection : AnimalCollection, IEnumerable<Turtle>
{
}

That way, when someone does foreach (var turtle in turtleCollection) var is automatically of type Turtle .这样,当有人执行foreach (var turtle in turtleCollection)时, var自动属于Turtle类型。 Without having implemented IEnumerable<Turtle> , the user would have had to type foreach (Turtle turtle in turtleCollection) .如果没有实现IEnumerable<Turtle> ,用户将不得不输入foreach (Turtle turtle in turtleCollection) Since I always type var everywhere I can, I'm sure I would eventually trip over the fact my turtle variable wasn't casted to the type I expected, which means the users of my API would probably stumble over this as well!由于我总是在任何我能输入的地方输入var ,我相信我最终会因为我的turtle变量没有被转换为我期望的类型而绊倒,这意味着我的 API 的用户也可能会为此绊倒!

So while I'm aware that having a type implement IEnumerable<T> on two adjacent types in an inheritance tree can cause issues , I'm wondering whether there could be hidden gotchas in using multiple IEnumerable<T> with types in the same inheritance hierarchy?因此,虽然我知道在 inheritance 树中的两个相邻类型上实现IEnumerable<T>类型可能会导致问题,但我想知道在使用多个IEnumerable<T>和同一 inheritance 中的类型时是否存在隐藏的陷阱等级制度?


EDIT: The most sensible solution here would be to have AnimalCollection<T> and then define TurtleCollection: AnimalCollection<Turtle> as has been pointed out by some of the existing comments/answers.编辑:这里最明智的解决方案是拥有AnimalCollection<T>然后定义TurtleCollection: AnimalCollection<Turtle>正如一些现有评论/答案所指出的那样。 However the reason my AnimalCollection is non-generic is that the type I'm calling AnimalCollection actually represents a node in a tree .然而,我的AnimalCollection是非泛型的原因是我调用AnimalCollection的类型实际上代表了tree中的一个节点 In actuality, I have types like实际上,我有类似的类型

class Node {}
class FooNode : Node {}
class BarNode : Node {}

class NodeCollection : Node {}
class FooNodeCollection : NodeCollection {}
class BarNodeCollection : NodeCollection {}

FooNodeCollection is constrained in its constructor to only take objects of type FooNode . FooNodeCollection在其构造函数中被限制为只接受FooNode类型的对象。 I then have visitors for accessing the nodes of a tree然后我让访问者访问树的节点

class NodeVisitor
{
    abstract void VisitFoo(FooNode foo);
    abstract void VisitBar(BarNode bar);
    abstract void VisitCollection(NodeCollection collection);
}

The exact type of node that is contained in the NodeCollection doesn't really matter as far as visitors are concerned;就访问者而言, NodeCollection中包含的确切节点类型并不重要。 all the visitor will do is simply iterate over its children and have them dispatch themselves to the visitor.访问者所要做的就是简单地遍历它的孩子并让他们将自己发送给访问者。

Potentially one solution then could be to have two types - NodeCollection which is used by the visitor, and NodeCollection<T> which derives from NodeCollection and is the one that actually implements IEnumerable<T> , however I'm also interested to know what types of issues could arise from utilizing multiple IEnumerable<T> in the way originally stated潜在的一种解决方案可能是有两种类型 - 访问者使用的NodeCollection NodeCollection是实际实现IEnumerable<T> NodeCollection<T>但是我也有兴趣知道什么类型的问题可能源于以最初陈述的方式使用多个IEnumerable<T>

I'd structure the code to use composition, not inheritance .我将构建代码以使用组合,而不是 inheritance

class Animal {}
class Turtle : Animal { public Color ShellColor {get;set;} }

abstract class AnimalCollection<T> where T : Animal
{
   public IEnumerable<T> Animals {get; set;} 
}

class TurtleCollection : AnimalCollection<Turtle> {}

(...)
foreach( var t in new TurtleCollection().Animals)
{
    Console.WriteLine(t.ShellColor);
}

(Update) (更新)

Oh, yeah, Why not inherit from List<T>?哦,是的, 为什么不从 List<T> 继承呢? is always a good read as well.也总是一本好书。

An additional answer to the question you linked to also hints at why multiple implementation of IEnumerable<T> for types in the same inheritance hierarchy would be bad.您链接到的问题的另一个答案还暗示了为什么IEnumerable<T>对同一 inheritance 层次结构中的类型的多个实现会很糟糕。 If you consider the scenario of the answer, trying to cast to the base type would leave the runtime making an undefined choice, which is something that you should, to quote the answer: Avoid, avoid, avoid .如果您考虑答案的场景,尝试强制转换为基本类型将使运行时做出未定义的选择,这是您应该引用的答案:避免,避免,避免

Beyond that, let me remind you of OfType<T> :除此之外,让我提醒您OfType<T>

foreach (var turtle in collection.OfType<Turtle>())
{
    //turtle is guaranteed to be of type Turtle.
    //If no Turtle objects exist in the collection,
    //the iteration will simply not execute!
}

And, finally, your posted code would definitely be clearer if you followed the suggestion of Federico Dipuma :最后,如果您遵循Federico Dipuma的建议,您发布的代码肯定会更清晰:

Just create a class AnimalCollection<TAnimal>: IEnumerable<TAnimal> where TAnimal: Animal .只需创建一个 class AnimalCollection<TAnimal>: IEnumerable<TAnimal> where TAnimal: Animal

EDIT编辑

Based on the updated question, my understanding is that the visitor does something along these lines:根据更新后的问题,我的理解是访问者做了以下事情:

public void VisitCollection(NodeCollection node)
{
    //The problem is that, here, there is no way to know
    //(without pattern-matching or type checks in general)
    //what node type the collection contains. However
    //it may not matter at all, so there should be no problem.
}

Your problem would arise wherever you would be coding with access to both IEnumerable<T> implementations, ie wherever you have access to the specific class type rather than the base type.无论您在哪里编码都可以访问IEnumerable<T>实现,即无论您可以访问特定的 class 类型而不是基本类型,都会出现您的问题。 The VisitCollection method is certainly not one of these cases, because within the scope of the method, you only know (cf. see) an IEnumerable<Node> and not an IEnumerable<FooNode> or otherwise. VisitCollection方法当然不是这些情况之一,因为在该方法的 scope 中,您只知道(参见)一个IEnumerable<Node>而不是一个IEnumerable<FooNode>或其他。 Therefore, you are not "in danger" inside this method.因此,您在此方法中没有“危险”。

If, however, your visitor needs to know the specific node type in case of collections, you would have:但是,如果您的访问者需要知道 collections 的具体节点类型,您将拥有:

public abstract class Visitor
{
    //As in your case...
    abstract void VisitFooNodeCollection(FooNodeCollection collection);
    abstract void VisitBarNodeCollection(BarNodeCollection collection);
}

In these cases, you might have trouble using var (I say "might" because it is more like "undefined behavior") because FooNodeCollection is both IEnumerable<Node> and IEnumerable<FooNode> (if I understand correctly).在这些情况下,您可能无法使用var (我说“可能”,因为它更像“未定义的行为”),因为FooNodeCollection既是IEnumerable<Node>又是IEnumerable<FooNode> (如果我理解正确的话)。 So, in that context, whenever you type var , you might have the problem you mention, ie the runtime will not know what to cast your object to.因此,在这种情况下,每当您键入var时,您可能会遇到您提到的问题,即运行时将不知道将您的 object 转换为什么。

However, the use of inherited (rather than adjacent) hierarchy types gives you the option to resolve the inconsistency using OfType<T> .但是,使用继承的(而不是相邻的)层次结构类型为您提供了使用OfType<T>解决不一致问题的选项。 If the runtime gets the enumerator of the base type, your objects are certainly of the base type, so they will all be enumerated.如果运行时得到基类型的枚举器,那么你的对象肯定是基类型的,所以它们都会被枚举。 If the enumerator of the derived type runs, of course your objects have been constrained (by the constructor), so your are still safe and they will definitely be of that specific type, hence all will be enumerated.如果派生类型的枚举器运行,当然你的对象已经被约束(由构造函数),所以你仍然是安全的,它们肯定是那个特定的类型,因此所有的都会被枚举。

In short, if you are casting to the correct type with OfType<T> , you should not have a problem, plus the var will be of the cast type T, so you will have compile-time analysis.简而言之,如果您使用OfType<T>转换为正确的类型,则应该没有问题,而且var将是转换类型 T,因此您将进行编译时分析。 However, because I think you are probably OK with the visitor and you only take into account what to do with a type only at the single-node level, you will probably not run into this type of problem, as you will be operating within the "collection-type-agnostic" VisitCollection scope.但是,因为我认为您可能对访问者没有问题,并且您只考虑在单节点级别如何处理类型,您可能不会遇到此类问题,因为您将在“集合类型不可知” VisitCollection In that case, though, I don't see why you would want to make the inheriting classes implement IEnumerable<DerivedType> as well.不过,在这种情况下,我不明白为什么要让继承类也实现IEnumerable<DerivedType>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM