Reputation: 69
I'm learning inheritance and have a few questions:
Class B
and class C
are different classes that inherit from A
.
Assuming I want to stack in the same list instances of class B
and C
,
what is the type of the list should be? List<object>
or List<A>
?
Why List<A>
can hold the data of class B
and C
if Class A
has fewer fields? I would expect that List<A>
will trim the extra fields of class B
and C
inside the list, but I saw a working example of it.
When looping the list with foreach
how can I tell if the object is class B
or C
?
Thank you!
Upvotes: 0
Views: 147
Reputation: 112334
The the type parameter of the list must be the nearest common ancestor of B
and C
: here List<A>
. List<object>
would also work, but at the cost that it is weakly typed and allows you add any kind of objects: strings integers, dates, persons. Which is probably not the intention here.
Class types are reference types. This means that the list only contains references to objects of different length, that are not themselves in the list. I.e. no truncation occurs. This is also the reason why value types can not be inherited. You cannot inherit from an int
or from a struct like DateTime
.
Let's imagine a better example for this question
public abstract class : Shape
{
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
}
public class Circle : Shape
{
public double Radius { get; set; }
}
Now, let us calculate the total area of shapes in a list List<shape> shapes
:
double totalArea = 0.0;
foreach (Shape shape in shapes) {
if (shape is Rectangle rect) {
totalArea += rect.Width * rect.Height;
} else if (shape is Circle circle) {
totalArea += circle.Radius * circle.Radius * Math.Pi;
}
}
or
double totalArea = 0.0;
foreach (Shape shape in shapes) {
switch (shape)
{
case Rectangle rect:
totalArea += rect.Width * rect.Height;
break;
case Circle circle:
totalArea += circle.Radius * circle.Radius * Math.Pi;
break;
}
}
But generally it is better if you don't need to know the type. E.g. Instead of using the switch
in the last example, let the classes themselves do the job
public abstract class : Shape
{
public abstract double Area { get; }
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double Area => Width * Height;
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double Area => Radius * Radius * Math.Pi;
}
Then the loop becomes
double totalArea = 0.0;
foreach (Shape shape in shapes) {
totalArea += shape.Area;
}
This is called polymorphism (multi-shaped). In this case shape
can be a Rectangle
or a Circle
. But you don't have to care. shape.Area
will automatically call Rectangle.Area
for Rectangle
objects and Circle.Area
for Circle
objects. This is the true power of object orientation, where as the solutions with if
or switch
are a procedural approach.
Upvotes: 2
Reputation: 5737
If you have a List<A>
, you can use it for each class of the type of A
and all derived classes. Lets take it to the real world:
If you have a definition Vehicle
(that's your A
). You could write a software for a car rental company which handles vehicles. They have all sorts for vehicles like one or more bikes (that's B
) and one or more cars (that's C
). Both are very different but both are vehicles.
By using the abstraction of a Vehicle
(A
), you can store bikes and cars in the same context.
So in a List<Vehicle>
, you could store bikes, cars, trucks and all sort of movable stuff. However, the definition Vehicle
will have the least common denominator like the kind of motor, the count of doors, etc. But you would (hopefully) not find truck-specific properties on that definition of vehicle which might not fit to others like a bike or a car.
Tracking bikes and cars as vehicles does not strip of the fields at all. However, they are treated as vehicles, that's why you would not see the specific properties until you cast each vehicle to its real type. There are some approaches to this, I'd like to highlight Pattern Matching in C# 7 which makes it really easy by now.
foreach (var v in vehicleList)
{
switch (v)
{
case Bike b:
// ... do bike specific things
break;
case Car c:
// ... do car specific things
break;
case Truck t:
// ... do truck specific things
break;
default:
WriteLine("<unknown>");
break;
}
}
Upvotes: 0
Reputation: 329
Its basic oop (object oriented proggreming) principall called polymorphisem
you shoud use listList<A>
when you put B
type object into List<A>
its like to look on B as A. so you can access th A fields that it inherits
you can use the is
statement.
for example if (someObject is B)
you can reed more here polymorphisem docs
Upvotes: 1