Sasha Vasserfirer
Sasha Vasserfirer

Reputation: 69

c# stacking different classes in the same list

I'm learning inheritance and have a few questions:

Thank you!

Upvotes: 0

Views: 147

Answers (3)

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112334

  1. 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.

  2. 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.

  3. 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

Waescher
Waescher

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

tomas
tomas

Reputation: 329

Its basic oop (object oriented proggreming) principall called polymorphisem

  1. you shoud use listList<A>

  2. 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

  3. you can use the is statement. for example if (someObject is B)

you can reed more here polymorphisem docs

Upvotes: 1

Related Questions