public async IEnumerable<ParentClass> ReadResultAsync
{
var results = new List<ParentClass>();
results.Add(ChildClassA);
results.Add(ChildClassB);
return results;
}
I do this process in service layer.
When I return results => IEnumerable<ParentClass>
to Controller and show the results after calling a request, I face a problem:
It only returns the properties which the ParentClass has.
Ex.
ParentClass has property A, B, C.
ChildClassA inherit ParentClass has property D, E.
ChildClassB inherit ParentClass has property F.
The results only can get properties A, B, C. (Which I expect ChildClassA (A, B, C, D, E) and ChildClassB (A, B, C, F) in the same list)
My temporary solution is to change ParentClass to object, but I don't think it's good.
public async IEnumerable<object> ReadResultAsync
{
var results = new List<object>();
results.Add(ChildClassA);
results.Add(ChildClassB);
return results;
}
Is there any solution can create a parent list that can contain all child classes (which I want)?
If you declare your returning object with type IEnumerable<ParentClass>
, the compiler cannot make any assumptions about the elements of this sequence, more than those guaranteed by the signature of ParentClass
.
In other words, the elements might each ultimately be one different derived type, but anyone consuming that object doesn't know (and should not know) what are the concrete types.
To understand better, you can search and learn about concepts like: Liskov substitution principle, Covariance/Contravariance and Interfaces.
If it makes sense, based on your app logic, to assume a specific derived type of an element and call derived-type specific methods on the object, you have a few options:
Get the element from the enumerable, and then cast to the derived type, like this
foreach (ParentClass pc in results) {
ChildClassA cca = (ChildClassA)pc; // could throw InvalidCastException
cca.D();
// ...
}
Note that this would fail with an InvalidCastException
if, for example, one of the elements is of type ChildClassB
and cannot be cast into ChildClassA
. You can mitigate this by doing a safe cast with the as
operator, and handle null
.
foreach (ParentClass pc in results) {
ChildClassA cca = pc as ChildClassA; // could end up being null
cca.D(); // could throw NullReferenceException
// ...
}
Use the is
operator to pattern match.
foreach (ParentClass pc in results) {
if (pc is ChildClassA cca) {
cca.D(); // would only reach here if it is indeed ChildClassA
}
// ...
}
try to cast the whole enumerable to a derived type with the LINQ Cast
extension method:
IEnumerable<ChildClassA> childAResults = results.Cast<ChildClassA>();
// could throw InvalidCastException
foreach (ChildClassA cca in childAResults) {
cca.D();
// ...
}
This has the same issue as option 1 above, it would throw InvalidCastException
if any element cannot be cast into ChildClassA
.
Filter the enumerable to only get elements of type ChildClassA (or derived), using the Linq method OfType
.
IEnumerable<ChildClassA> childAResults = results.OfType<ChildClassA>();
foreach (ChildClassA cca in childAResults) { // safe
cca.D();
// ...
}
Hack with dynamic
or object
, which I do not recommend for this case by any means.
Review your design. Trying to assume a derived type on an object is commonly a code smell, that your parent class abstraction is weak or that you are leaking implementation details and braking encapsulation. Check the SOLID principles.