2017-06-20 134 views
2

我的同事告訴我,有可能將List<BaseClass>轉換爲List<DerivedClass>(他們沒有給我示例)。我不認爲有可能將一個父類轉換成一個子類(但我確實知道可以用相反的方法做到這一點)。我曾經看到過一些與人相關的問題,他們說這是不可能完成的,人們都在說它可以 - 所有提議的解決方案都不適合我。我總是得到:將基類轉換爲派生類列表 - 可能嗎?

System.InvalidCastException. 
Unable to cast object of type 'BaseClass' to 'ChildClass' 

這是絕對可行的嗎?如果是這樣,我做錯了什麼?這裏是我的代碼(簡化這個問題的目的):

父類:

public class ParentClass 
{ 
    public string Name = ""; 
} 

子類:

public class ChildClass: ParentClass 
{ 
    public string Date = ""; 
} 

轉換:

List<ParentClass> parentList = ServiceApi.GetParentClassList().ToList(); 
List<ChildClass> childList = parentList.Cast<ChildClass>().ToList(); 

我的實際代碼明顯更復雜,但應適用相同的原則。

+0

的[是否有可能基類對象分配給在C#的顯式類型轉換派生類引用?]可能的複製(https://stackoverflow.com/questions/729527/is-it-possible -to-assign-a-base-class-object-to-a-derived-class-reference-with-a) –

+2

@RufusL我不認爲重複是完全重複的。 OP的問題是不是真的一樣 –

+1

[C#:將基類轉換爲子類]的可能重複(https://stackoverflow.com/questions/16534253/c-sharp-converting-base-class-to-child-class ) –

回答

1

Cast<T>方法將投射操作應用於輸入序列的所有元素。它的工作原理只有當你可以做以下的序列中的每個元素,而不會造成異常:

ParentClass p = ... 
ChildClass c = (ChildClass)p; 

除非p分配的ChildClass或其子類的一個實例這將無法正常工作。看起來,在你的情況下,從服務器API返回的數據包含ParentClassChildClass以外的其中一個子類的對象。

您可以通過構建ChildClass情況下解決這個問題,假設你有從服務器足夠的信息:

List<ChildClass> childList = parentList 
    .Select(parent => new ChildClass(parent.Name, ... /* the remaining fields */)) 
    .ToList(); 
+0

這真的是最差的答案,不知道爲什麼OP選擇接受 –

+0

@CamiloTerevinto我最好的猜測是,這是唯一的答案,顯示OP如何處理的情況下,如果沒有'ChildClass'對象' childList',當你從服務器收到DTO對象時,這很可能發生。 – dasblinkenlight

+0

這不完全正確,我的答案還處理沒有ChildClass對象的空列表和列表。事實上,所有其他3個答案處理這個問題 –

6

從某種意義上說,你都是對的。

比方說,你有類型AnimalCat(繼承動物)和Dog(繼承動物)。 A List<Animal>可以包含貓和狗,但List<Dog>不能包含貓。如果您有一個包含Cat和Dog對象的List<Animal>,則您所做的任何事情都不會讓您生成包含原始列表中所有內容的List<Dog>

所以從這個意義上說,你是正確的,你不能從List<ParentClass>生產List<DerivedClass>

但是有些時候,作爲程序員,您有關於如何使用對編譯器不可用的對象的信息。如果(且僅當),您可以知道某個特定List<Animal>實例只包含Dog對象,你總是可以只說:

List<Dog> dogList = myAnimalListWithOnlyDogs.Cast<Dog>().ToList(); 

還是那句話:這隻能在你能保證每一個對象在列表實例可轉換爲Dog對象。如果一個Cat對象以某種方式工作,它就會進入你的列表,最終在運行時會出現異常。儘管如此,這樣的代碼並不罕見。

從這個意義上說,同事是對的。當談到真正的工作代碼時,人們無論如何都會產生很大的效果,並且很好地擺脫它,儘管純粹主義者可能會認爲這是代碼氣味,表明需要使用組合而不是繼承。

此外,有時候List<Animal>實例同時包含Cat和Dog對象,並且您只關心Dog對象。在這種情況下,您可以這樣做:

List<Dog> dogList = myAnimalList.OfType<Dog>().ToList(); 

此模式在使用WinForms控件時特別常見。

請注意,在這兩種情況下,您正在使用新序列。向序列中添加或刪除對象不會更改原始內容。所以從這個意義上說,你有不是轉換成原來的一樣多創建一個替代品。但是,這個序列包含了相同的對象,所以編輯一個Dog對象中的一個屬性在新序列中更改原來的對象,因爲它是同一個對象實例。

1

沒有測試,但這應該工作:

List<ParentClass> parentList = ServiceApi.GetParentClassList().ToList(); 
List<ChildClass> childList = parentList 
    .Where(x => x is ChildClass) 
    .Select(x => x as ChildClass) 
    .ToList(); 

你也可以使用OfType在評論中提到:

List<ChildClass> childList = parentList 
    .OfType<ChildClass>() 
    .ToList(); 

看到這個.NET小提琴:https://dotnetfiddle.net/gDsNKi

+0

在這個例子(和小提琴),這隻適用於列表有一個ChildClass已經在它。如果列表裏面沒有任何ChildClass,它將返回0.是否可以處理列表僅包含BaseClass對象的情況?然後,將該列表轉換爲列表? – Roka545

+1

@ Roka545在列表中沒有ChildClass實例的情況下,Where將返回一個新的'List ',爲空。 (基本上,它實例化一個新列表並添加任何過濾器返回true的對象,因此無論過濾器如何,您總是會有一個新列表) –

1

如果您在施放失敗時願意失去物品,你可以使用OfType()方法僅返回施放成功的物品。

var children = parentList.OfType<ChildClass>().ToList();