2011-03-08 35 views
6

如何向下轉換對象列表,以便將列表中的每個對象向下轉換爲派生類的對象?在C中向下轉換對象列表#

這是場景。

我有一個基類的基礎項目一List,以及兩班從它繼承:

public class BaseClass 
{ 
    public List<BaseItem> items; 
    protected BaseClass() 
    { 
     // some code to build list of items 
    } 
} 
public class DerivedClass : BaseClass 
{ 
    public DerivedClass : base() {} 
} 
public class AnotherDerivedClass : BaseClass 
{ 
    public AnotherDerivedClass : base() {} 
} 

public class BaseItem {} 
public class DerivedItem : BaseItem {} 
public class AnotherDerivedItem : BaseItem {} 

的想法是不會有重複建設的項目列表中所需的代碼。 BaseItem具有我需要的所有基本功能,並且我總是可以將BaseItem降頻爲派生項目之一。

問題出現在我列出它們的時候。 BaseItemListBaseClass中聲明,因爲所有的派生類必須擁有它。但是在運行時訪問它時,我似乎無法向派生類下注。

+0

或許你也應該仔細研究的接口。 – 2011-03-08 14:59:45

+0

或AS和IS關鍵字。 – ThatBlairGuy 2011-03-08 15:03:04

+0

或bog標準多態:基類中的虛方法,它在派生類中重寫。不需要向下轉換,因爲您使用基類類型來調用重寫的方法。 – Polyfun 2011-03-08 15:14:00

回答

5

使用LINQ:

var baseList = new List<BaseClass>(); 
    var derivedList = baseList.Cast<DerivedClass>(); 

注:說完就垂頭喪氣,通常是一個「氣味」,並表示繼承層次是錯誤的,或者錯誤地實施。擁有基類的想法是,您可以將所有子類視爲超類而不必向下轉換爲單個子類類型。

而不是Cast您可能希望使用OfType從超類的集合中「撈出」某些派生類。但是,再次,應該沒有必要這樣做。

問問自己,爲什麼你需要有一個子類 - 也許你需要移動一些功能到基類?

+0

這一個完全是我所要求的。但其他答案和評論讓我想知道我是否可以完全避免這樣的事情。 – Farinha 2011-03-08 15:56:33

+0

@Farinha - 添加備註 – 2011-03-08 16:01:11

+0

我可以發誓這個解決方案昨晚工作...並且它不再... – Farinha 2011-03-09 08:32:52

5

我相信你想要做的是使用泛型:

public class BaseClass<T> where T: BaseItem, new() 
{ 
    public List<T> items; 
    protected BaseClass() 
    { 
     items = new List<T>(); 
     T item = new T(); 
     item.SomePropertyOfBaseItem=something; 
     items.Add(item); 
    } 
} 

public class DerivedClass: BaseClass<DerivedItem> 

這將導致DerivedClassitemsList<DerivedItem>where強制規定只能使用從BaseItem派生的類型。

編輯:「向下轉換」,將類型轉換爲派生類型,實際上並不是您在此嘗試執行的操作。您的意圖是派生的列表對象按設計使用特定的派生項目類型,並且可能希望將派生類型的實例化對象存儲在派生列表類中。

因此,如果不使用泛型,這可以很好地工作:List<BaseItem>完全能夠存儲從BaseItem派生的任何項目。但是,您必須使用強制轉換(如其他答案中所述)從列表中引用這些對象才能訪問派生屬性。但是,這只是將一個對象「鑄造」爲真正的類型。泛型爲您提供了一種直接提供對這些對象的強類型訪問的方法。

基本上,將對象存儲在作爲對象超類的容器中並不會改變對象的任何內容 - 它只會改變代碼引用它的方式,使其看起來更簡單它來源於它。

+0

這裏的問題是在BaseClass中有items屬性是很有用的,否則我將無法獲得BaseClass的構造函數來構建它。 – Farinha 2011-03-08 15:58:33

+0

爲什麼不呢? 「List 」你的構造函數會做什麼,它不能像列表那樣容易?由於'where'子句,編譯器實際上會像'BaseItem'一樣處理'T'。 – 2011-03-08 16:05:02

+0

順便說一句 - 泛型可能是自1.0以來C#中最有用的一個。我知道,我會從喜歡linq或者'='的人那裏得到一些不同意見。但是,儘管您可以創建使用強制轉換來完成相同任務的代碼,但通過使用泛型,您可以強制鍵入內部成員並返回值,從而使代碼更具可讀性,易理解性和抗bug性。 – 2011-03-08 16:09:12

0
public class Zoo 
{ 
    public List<Animal> items; 
    protected BaseClass() 
    {   // some code to build list of items  } 
} 

public class PettingZoo : Zoo 
{ 
    public PettingZoo : base() {} 
} 

public class CatComplex : Zoo 
{ 
    public CatComplex : base() {} 
} 

public class Animal {} 
public class Sheep : Animal {} 
public class Tiger : Animal {} 

...

PettingZoo myZoo = new PettingZoo(); 
myZoo.items.Add(new Tiger()); 

PettingZoo不能與動物園互換,因爲PettingZoo應限制動物的類型。因此,此設計不符合the Liskov substitution principle

0

您還可以使用LINQ通過基類的元素進行迭代喪氣的每一個元素,像這樣:

var baseList = new List<BaseClass>(); 
var derivedList = new List<DerivedClass>(); 
baseList.ForEach(v => derivedList.Add((DerivedClass)v)); 

如果需要鑄造前檢查派生類型的每個元素:

var baseList = new List<BaseClass>(); 
var derivedList = new List<DerivedClass>(); 
baseList.OfType<DerivedClass>().ToList().ForEach(v => derivedList.Add(v)); 

欲瞭解更多信息,看看下面的文章: