[英]Override IEnumerable<T> in More Derived Class - Any Potential Issues?
我有一個抽象的自定義集合類型,我們將調用AnimalCollection
來存儲Animal
對象的集合。 需要存儲在動物集合中的動物非常特殊,以至於它們擁有自己的AnimalCollection
實現,用於存儲特定於該類型集合的附加信息,例如TurtleCollection
、 GiraffeCollection
等。
由於我們在這里都是 collections,我認為AnimalCollection
應該實現IEnumerable<Animal>
以便用戶可以迭代它是有道理的。 然而,在我們更多派生類的情況下,實現IEnumerable<Turtle>
、 IEnumerable<Giraffe>
等更有意義。
class Animal {}
class Turtle : Animal {}
abstract class AnimalCollection : IEnumerable<Animal>
{
}
class TurtleCollection : AnimalCollection, IEnumerable<Turtle>
{
}
這樣,當有人執行foreach (var turtle in turtleCollection)
時, var
自動屬於Turtle
類型。 如果沒有實現IEnumerable<Turtle>
,用戶將不得不輸入foreach (Turtle turtle in turtleCollection)
。 由於我總是在任何我能輸入的地方輸入var
,我相信我最終會因為我的turtle
變量沒有被轉換為我期望的類型而絆倒,這意味着我的 API 的用戶也可能會為此絆倒!
因此,雖然我知道在 inheritance 樹中的兩個相鄰類型上實現IEnumerable<T>
類型可能會導致問題,但我想知道在使用多個IEnumerable<T>
和同一 inheritance 中的類型時是否存在隱藏的陷阱等級制度?
編輯:這里最明智的解決方案是擁有AnimalCollection<T>
然后定義TurtleCollection: AnimalCollection<Turtle>
正如一些現有評論/答案所指出的那樣。 然而,我的AnimalCollection
是非泛型的原因是我調用AnimalCollection
的類型實際上代表了tree中的一個節點。 實際上,我有類似的類型
class Node {}
class FooNode : Node {}
class BarNode : Node {}
class NodeCollection : Node {}
class FooNodeCollection : NodeCollection {}
class BarNodeCollection : NodeCollection {}
FooNodeCollection
在其構造函數中被限制為只接受FooNode
類型的對象。 然后我讓訪問者訪問樹的節點
class NodeVisitor
{
abstract void VisitFoo(FooNode foo);
abstract void VisitBar(BarNode bar);
abstract void VisitCollection(NodeCollection collection);
}
就訪問者而言, NodeCollection
中包含的確切節點類型並不重要。 訪問者所要做的就是簡單地遍歷它的孩子並讓他們將自己發送給訪問者。
潛在的一種解決方案可能是有兩種類型 - 訪問者使用的NodeCollection
NodeCollection
是實際實現IEnumerable<T>
NodeCollection<T>
但是我也有興趣知道什么類型的問題可能源於以最初陳述的方式使用多個IEnumerable<T>
我將構建代碼以使用組合,而不是 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);
}
(更新)
哦,是的, 為什么不從 List<T> 繼承呢? 也總是一本好書。
您鏈接到的問題的另一個答案還暗示了為什么IEnumerable<T>
對同一 inheritance 層次結構中的類型的多個實現會很糟糕。 如果您考慮答案的場景,嘗試強制轉換為基本類型將使運行時做出未定義的選擇,這是您應該引用的答案:避免,避免,避免。
除此之外,讓我提醒您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!
}
最后,如果您遵循Federico Dipuma的建議,您發布的代碼肯定會更清晰:
只需創建一個 class AnimalCollection<TAnimal>: IEnumerable<TAnimal> where TAnimal: Animal
。
根據更新后的問題,我的理解是訪問者做了以下事情:
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.
}
無論您在哪里編碼都可以訪問IEnumerable<T>
實現,即無論您可以訪問特定的 class 類型而不是基本類型,都會出現您的問題。 VisitCollection
方法當然不是這些情況之一,因為在該方法的 scope 中,您只知道(參見)一個IEnumerable<Node>
而不是一個IEnumerable<FooNode>
或其他。 因此,您在此方法中沒有“危險”。
但是,如果您的訪問者需要知道 collections 的具體節點類型,您將擁有:
public abstract class Visitor
{
//As in your case...
abstract void VisitFooNodeCollection(FooNodeCollection collection);
abstract void VisitBarNodeCollection(BarNodeCollection collection);
}
在這些情況下,您可能無法使用var
(我說“可能”,因為它更像“未定義的行為”),因為FooNodeCollection
既是IEnumerable<Node>
又是IEnumerable<FooNode>
(如果我理解正確的話)。 因此,在這種情況下,每當您鍵入var
時,您可能會遇到您提到的問題,即運行時將不知道將您的 object 轉換為什么。
但是,使用繼承的(而不是相鄰的)層次結構類型為您提供了使用OfType<T>
解決不一致問題的選項。 如果運行時得到基類型的枚舉器,那么你的對象肯定是基類型的,所以它們都會被枚舉。 如果派生類型的枚舉器運行,當然你的對象已經被約束(由構造函數),所以你仍然是安全的,它們肯定是那個特定的類型,因此所有的都會被枚舉。
簡而言之,如果您使用OfType<T>
轉換為正確的類型,則應該沒有問題,而且var
將是轉換類型 T,因此您將進行編譯時分析。 但是,因為我認為您可能對訪問者沒有問題,並且您只考慮在單節點級別如何處理類型,您可能不會遇到此類問題,因為您將在“集合類型不可知” VisitCollection
。 不過,在這種情況下,我不明白為什么要讓繼承類也實現IEnumerable<DerivedType>
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.