public async IEnumerable<ParentClass> ReadResultAsync
{
var results = new List<ParentClass>();
results.Add(ChildClassA);
results.Add(ChildClassB);
return results;
}
我在服務層執行此程序。當我將 results => 回傳IEnumerable<ParentClass>到 Controller 并在呼叫請求后顯示結果時,我遇到了一個問題:它只回傳 ParentClass 具有的屬性。
前任。ParentClass 具有屬性 A、B、C。
ChildClassA 繼承 ParentClass 具有屬性 D、E。
ChildClassB 繼承 ParentClass 具有屬性 F。
結果只能得到屬性A,B,C。(我期望ChildClassA(A,B,C,D,E)和ChildClassB(A,B,C,F)在同一個串列中)
我的臨時解決方案是將 ParentClass 更改為 object,但我認為這不是很好。
public async IEnumerable<object> ReadResultAsync
{
var results = new List<object>();
results.Add(ChildClassA);
results.Add(ChildClassB);
return results;
}
是否有任何解決方案可以創建一個包含所有子類(我想要的)的父串列?
uj5u.com熱心網友回復:
ParentClass正如您所證明的,您的串列已經可以包含派生自 的任何類的實體。問題,如果你可以這么稱呼它,是使用串列的代碼除了知道它們是 type 之外,對物件一無所知ParentClass,因此它只能訪問該型別的成員。當它不知道它們存在時,它怎么可能訪問其他方法?改用object沒有幫助,因為這樣您仍然可以訪問更少的成員。
您可以使用dynamic,這將允許您在使用代碼中指定任何成員,但隨后您將失去所有型別安全性,因此您通常應該盡可能避免這種情況。它基本上提供了 VB 一直擁有的相同的后期系結功能。
如果派生型別的數量很少,那么您最好的選擇可能是使用條件轉換,例如
foreach (var p in await ReadResultAsync())
{
if (p is ChildClassA ca)
{
// Use ca here.
}
else if(p is ChildClassB cb)
{
// Use cb here.
}
}
uj5u.com熱心網友回復:
最簡單的解決方案是使用動態或物件方式。型別安全無關緊要,因為您只發出資料。
您可以在通過創建 DTO(資料傳輸物件)來發出物件時使用映射技術,或者您可以在發出結果之前使用 Linq。但是重新映射需要處理資源。
一個 linq 解決方案的示例可能是:
public async IEnumerable<object> ReadResultAsync
{
var results = new List<object>();
results.Add(ChildClassA);
results.Add(ChildClassB);
return await (from n in results
select new {
//Here you assign the properties that you want and
//cast the more abstract type to its derived type,
//and then assign more properties.
} ).ToListAsync();
}
您創建一個擴展方法,通過使用反射和一些運算式樹來執行該程序。這將使映射更加通用和可重用。
另一種解決方案是使用介面而不是繼承。
最后一個介面將具有所有屬性。A、B、C、D、E、F。最后一個也是最衍生的介面將從更抽象的介面中獲取其屬性。因此,例如 interface1 將具有 A、B、C,而 interface2 將具有 E 和 F。
這種方法的問題在于它與 Liskov 替換程序相矛盾,因為某些派生類不會具有某些屬性的實作。
uj5u.com熱心網友回復:
如果你用 type 宣告你的回傳物件IEnumerable<ParentClass>,編譯器不能對這個序列的元素做任何假設,比簽名所保證的要多ParentClass。
換句話說,每個元素最終可能都是一種不同的派生型別,但是使用該物件的任何人都不知道(也不應該知道)具體型別是什么。
為了更好地理解,您可以搜索并了解以下概念:Liskov 替換原理、協方差/逆變和介面。
如果根據您的應用程式邏輯假設元素的特定派生型別并在物件上呼叫派生型別特定方法是有意義的,那么您有幾個選擇:
從可列舉中獲取元素,然后強制轉換為派生型別,像這樣
foreach (ParentClass pc in results) { ChildClassA cca = (ChildClassA)pc; // could throw InvalidCastException cca.D(); // ... }請注意,這將失敗
InvalidCastException,例如,如果元素之一是型別ChildClassB并且不能強制轉換為ChildClassA.as您可以通過對操作員進行安全強制轉換來緩解這種情況,并處理null.foreach (ParentClass pc in results) { ChildClassA cca = pc as ChildClassA; // could end up being null cca.D(); // could throw NullReferenceException // ... }使用
is運算子進行模式匹配。foreach (ParentClass pc in results) { if (pc is ChildClassA cca) { cca.D(); // would only reach here if it is indeed ChildClassA } // ... }嘗試使用 LINQ
Cast擴展方法將整個可列舉型別轉換為派生型別:IEnumerable<ChildClassA> childAResults = results.Cast<ChildClassA>(); // could throw InvalidCastException foreach (ChildClassA cca in childAResults) { cca.D(); // ... }這與上面的選項 1 具有相同的問題,
InvalidCastException如果任何元素無法轉換為ChildClassA.使用 Linq 方法過濾可列舉以僅獲取 ChildClassA(或派生)型別的元素
OfType。IEnumerable<ChildClassA> childAResults = results.OfType<ChildClassA>(); foreach (ChildClassA cca in childAResults) { // safe cca.D(); // ... }dynamic使用or破解object,我不建議在這種情況下使用任何方式。檢查您的設計。試圖假設一個物件的派生型別通常是一種代碼味道,您的父類抽象很弱,或者您正在泄漏實作細節和制動封裝。檢查SOLID 原則。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/515861.html
下一篇:Dart抽象靜態方法
