c# stacking different classes in the same list
I'm learning inheritance and have a few questions:
- Assuming I have a base class A.
Class
B
and classC
are different classes that inherit fromA
.
Assuming I want to stack in the same list instances of class
B
andC
,
what is the type of the list should be?List<object>
orList<A>
?Why
List<A>
can hold the data of classB
andC
if ClassA
has fewer fields? I would expect thatList<A>
will trim the extra fields of classB
andC
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 classB
orC
?
Thank you!
c# list inheritance
add a comment |
I'm learning inheritance and have a few questions:
- Assuming I have a base class A.
Class
B
and classC
are different classes that inherit fromA
.
Assuming I want to stack in the same list instances of class
B
andC
,
what is the type of the list should be?List<object>
orList<A>
?Why
List<A>
can hold the data of classB
andC
if ClassA
has fewer fields? I would expect thatList<A>
will trim the extra fields of classB
andC
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 classB
orC
?
Thank you!
c# list inheritance
The point of inheritance is to reuse code. The point of polymorphism is to hide the implementation from the calling code by providing a common interface. So, if you are doing proper OOP (SOLID principles are a good starting point), then placing instances of different classes into the same list only makes sense if they share the same interface. For the caller, all the object in that list will be instances ofA
. If you need to check the type of the object inside theforeach
loop, you're doing it wrong.
– Groo
Nov 25 '18 at 21:20
An Interface is your friend. Anyway, if you add aB
orC
class to aList<A>
and checkList<A>[N] is B
,List<A>[N] is C
, the test will return the actual class stored at that index. You can then cast to the right class (it may not be even necessary, depending on what you're doing with it).
– Jimi
Nov 25 '18 at 21:26
2.class A
is a Reference Type, hence only references are stored in that list (like a reference counting C++ pointer)
– Dennis Kuypers
Nov 25 '18 at 23:07
add a comment |
I'm learning inheritance and have a few questions:
- Assuming I have a base class A.
Class
B
and classC
are different classes that inherit fromA
.
Assuming I want to stack in the same list instances of class
B
andC
,
what is the type of the list should be?List<object>
orList<A>
?Why
List<A>
can hold the data of classB
andC
if ClassA
has fewer fields? I would expect thatList<A>
will trim the extra fields of classB
andC
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 classB
orC
?
Thank you!
c# list inheritance
I'm learning inheritance and have a few questions:
- Assuming I have a base class A.
Class
B
and classC
are different classes that inherit fromA
.
Assuming I want to stack in the same list instances of class
B
andC
,
what is the type of the list should be?List<object>
orList<A>
?Why
List<A>
can hold the data of classB
andC
if ClassA
has fewer fields? I would expect thatList<A>
will trim the extra fields of classB
andC
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 classB
orC
?
Thank you!
c# list inheritance
c# list inheritance
edited Nov 25 '18 at 21:16
Olivier Jacot-Descombes
68.6k890141
68.6k890141
asked Nov 25 '18 at 20:57
Sasha VasserfirerSasha Vasserfirer
73
73
The point of inheritance is to reuse code. The point of polymorphism is to hide the implementation from the calling code by providing a common interface. So, if you are doing proper OOP (SOLID principles are a good starting point), then placing instances of different classes into the same list only makes sense if they share the same interface. For the caller, all the object in that list will be instances ofA
. If you need to check the type of the object inside theforeach
loop, you're doing it wrong.
– Groo
Nov 25 '18 at 21:20
An Interface is your friend. Anyway, if you add aB
orC
class to aList<A>
and checkList<A>[N] is B
,List<A>[N] is C
, the test will return the actual class stored at that index. You can then cast to the right class (it may not be even necessary, depending on what you're doing with it).
– Jimi
Nov 25 '18 at 21:26
2.class A
is a Reference Type, hence only references are stored in that list (like a reference counting C++ pointer)
– Dennis Kuypers
Nov 25 '18 at 23:07
add a comment |
The point of inheritance is to reuse code. The point of polymorphism is to hide the implementation from the calling code by providing a common interface. So, if you are doing proper OOP (SOLID principles are a good starting point), then placing instances of different classes into the same list only makes sense if they share the same interface. For the caller, all the object in that list will be instances ofA
. If you need to check the type of the object inside theforeach
loop, you're doing it wrong.
– Groo
Nov 25 '18 at 21:20
An Interface is your friend. Anyway, if you add aB
orC
class to aList<A>
and checkList<A>[N] is B
,List<A>[N] is C
, the test will return the actual class stored at that index. You can then cast to the right class (it may not be even necessary, depending on what you're doing with it).
– Jimi
Nov 25 '18 at 21:26
2.class A
is a Reference Type, hence only references are stored in that list (like a reference counting C++ pointer)
– Dennis Kuypers
Nov 25 '18 at 23:07
The point of inheritance is to reuse code. The point of polymorphism is to hide the implementation from the calling code by providing a common interface. So, if you are doing proper OOP (SOLID principles are a good starting point), then placing instances of different classes into the same list only makes sense if they share the same interface. For the caller, all the object in that list will be instances of
A
. If you need to check the type of the object inside the foreach
loop, you're doing it wrong.– Groo
Nov 25 '18 at 21:20
The point of inheritance is to reuse code. The point of polymorphism is to hide the implementation from the calling code by providing a common interface. So, if you are doing proper OOP (SOLID principles are a good starting point), then placing instances of different classes into the same list only makes sense if they share the same interface. For the caller, all the object in that list will be instances of
A
. If you need to check the type of the object inside the foreach
loop, you're doing it wrong.– Groo
Nov 25 '18 at 21:20
An Interface is your friend. Anyway, if you add a
B
or C
class to a List<A>
and check List<A>[N] is B
, List<A>[N] is C
, the test will return the actual class stored at that index. You can then cast to the right class (it may not be even necessary, depending on what you're doing with it).– Jimi
Nov 25 '18 at 21:26
An Interface is your friend. Anyway, if you add a
B
or C
class to a List<A>
and check List<A>[N] is B
, List<A>[N] is C
, the test will return the actual class stored at that index. You can then cast to the right class (it may not be even necessary, depending on what you're doing with it).– Jimi
Nov 25 '18 at 21:26
2.
class A
is a Reference Type, hence only references are stored in that list (like a reference counting C++ pointer)– Dennis Kuypers
Nov 25 '18 at 23:07
2.
class A
is a Reference Type, hence only references are stored in that list (like a reference counting C++ pointer)– Dennis Kuypers
Nov 25 '18 at 23:07
add a comment |
3 Answers
3
active
oldest
votes
Its basic oop (object oriented proggreming) principall called polymorphisem
you shoud use list
List<A>
when you put
B
type object intoList<A>
its like to look on B as A. so you can access th A fields that it inheritsyou can use the
is
statement.
for exampleif (someObject is B)
you can reed more here polymorphisem docs
add a comment |
The the type parameter of the list must be the nearest common ancestor of
B
andC
: hereList<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 likeDateTime
.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.
Thank you very much for the answers! Regarding question 3, you showed how we can access the same methode (differently implemented) of each class without the if or switch statment. But how to access specific properties that only class B and C are possessed?
– Sasha Vasserfirer
Nov 26 '18 at 4:46
You have two possibilities: 1. By using these if or switch. 2. By using these properties in common methods or other properties. E.g. If the base class is aShape
with two derived classesRectangle
withLength
andWidth
properties andCircle
with aRadius
, then calculate the area in a common propertyArea
instead of accessing the new properties from outside with if or switch.
– Olivier Jacot-Descombes
Nov 26 '18 at 13:53
I included a better example in my answer.
– Olivier Jacot-Descombes
Nov 26 '18 at 15:32
add a comment |
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;
}
}
1
Pattern matching is neat, but OOP was invented precisely to avoid this type of code.
– Groo
Nov 25 '18 at 21:23
Good point, actually. I just wanted to show that the values are not lost if you recover the real type from the common vehicle list.
– Waescher
Nov 25 '18 at 21:24
@Groo - "invented precisely to avoid this type of code" - What? No, it was invented to precisely allow this kind of code. Do you have a reference to avoiding it?
– Enigmativity
Nov 25 '18 at 21:39
1
@Enigmativity: I am not sure what you are referring to with "precisely allow". Polymorphism means abstracting the actual object type and hiding the actual implementation through its interface (which is violated in this example which knows specifics of each subclass). Single responsibility principle states that a class should have single reason to change (violated here, you will need to update eachswitch
/case
in your program when you add aBoat
). This code is basically the school example for refactoring into OO code.
– Groo
Nov 25 '18 at 22:15
@Groo - OOP wasn't invented with the SOLID principles. They came along later. OOP was invented to allow polymorphism. When I said "precisely allow" I was referring to your line "invented precisely to avoid this type of code".
– Enigmativity
Nov 25 '18 at 22:19
|
show 4 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53471900%2fc-sharp-stacking-different-classes-in-the-same-list%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Its basic oop (object oriented proggreming) principall called polymorphisem
you shoud use list
List<A>
when you put
B
type object intoList<A>
its like to look on B as A. so you can access th A fields that it inheritsyou can use the
is
statement.
for exampleif (someObject is B)
you can reed more here polymorphisem docs
add a comment |
Its basic oop (object oriented proggreming) principall called polymorphisem
you shoud use list
List<A>
when you put
B
type object intoList<A>
its like to look on B as A. so you can access th A fields that it inheritsyou can use the
is
statement.
for exampleif (someObject is B)
you can reed more here polymorphisem docs
add a comment |
Its basic oop (object oriented proggreming) principall called polymorphisem
you shoud use list
List<A>
when you put
B
type object intoList<A>
its like to look on B as A. so you can access th A fields that it inheritsyou can use the
is
statement.
for exampleif (someObject is B)
you can reed more here polymorphisem docs
Its basic oop (object oriented proggreming) principall called polymorphisem
you shoud use list
List<A>
when you put
B
type object intoList<A>
its like to look on B as A. so you can access th A fields that it inheritsyou can use the
is
statement.
for exampleif (someObject is B)
you can reed more here polymorphisem docs
answered Nov 25 '18 at 21:28
tomastomas
10011
10011
add a comment |
add a comment |
The the type parameter of the list must be the nearest common ancestor of
B
andC
: hereList<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 likeDateTime
.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.
Thank you very much for the answers! Regarding question 3, you showed how we can access the same methode (differently implemented) of each class without the if or switch statment. But how to access specific properties that only class B and C are possessed?
– Sasha Vasserfirer
Nov 26 '18 at 4:46
You have two possibilities: 1. By using these if or switch. 2. By using these properties in common methods or other properties. E.g. If the base class is aShape
with two derived classesRectangle
withLength
andWidth
properties andCircle
with aRadius
, then calculate the area in a common propertyArea
instead of accessing the new properties from outside with if or switch.
– Olivier Jacot-Descombes
Nov 26 '18 at 13:53
I included a better example in my answer.
– Olivier Jacot-Descombes
Nov 26 '18 at 15:32
add a comment |
The the type parameter of the list must be the nearest common ancestor of
B
andC
: hereList<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 likeDateTime
.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.
Thank you very much for the answers! Regarding question 3, you showed how we can access the same methode (differently implemented) of each class without the if or switch statment. But how to access specific properties that only class B and C are possessed?
– Sasha Vasserfirer
Nov 26 '18 at 4:46
You have two possibilities: 1. By using these if or switch. 2. By using these properties in common methods or other properties. E.g. If the base class is aShape
with two derived classesRectangle
withLength
andWidth
properties andCircle
with aRadius
, then calculate the area in a common propertyArea
instead of accessing the new properties from outside with if or switch.
– Olivier Jacot-Descombes
Nov 26 '18 at 13:53
I included a better example in my answer.
– Olivier Jacot-Descombes
Nov 26 '18 at 15:32
add a comment |
The the type parameter of the list must be the nearest common ancestor of
B
andC
: hereList<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 likeDateTime
.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.
The the type parameter of the list must be the nearest common ancestor of
B
andC
: hereList<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 likeDateTime
.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.
edited Nov 26 '18 at 15:37
answered Nov 25 '18 at 21:34
Olivier Jacot-DescombesOlivier Jacot-Descombes
68.6k890141
68.6k890141
Thank you very much for the answers! Regarding question 3, you showed how we can access the same methode (differently implemented) of each class without the if or switch statment. But how to access specific properties that only class B and C are possessed?
– Sasha Vasserfirer
Nov 26 '18 at 4:46
You have two possibilities: 1. By using these if or switch. 2. By using these properties in common methods or other properties. E.g. If the base class is aShape
with two derived classesRectangle
withLength
andWidth
properties andCircle
with aRadius
, then calculate the area in a common propertyArea
instead of accessing the new properties from outside with if or switch.
– Olivier Jacot-Descombes
Nov 26 '18 at 13:53
I included a better example in my answer.
– Olivier Jacot-Descombes
Nov 26 '18 at 15:32
add a comment |
Thank you very much for the answers! Regarding question 3, you showed how we can access the same methode (differently implemented) of each class without the if or switch statment. But how to access specific properties that only class B and C are possessed?
– Sasha Vasserfirer
Nov 26 '18 at 4:46
You have two possibilities: 1. By using these if or switch. 2. By using these properties in common methods or other properties. E.g. If the base class is aShape
with two derived classesRectangle
withLength
andWidth
properties andCircle
with aRadius
, then calculate the area in a common propertyArea
instead of accessing the new properties from outside with if or switch.
– Olivier Jacot-Descombes
Nov 26 '18 at 13:53
I included a better example in my answer.
– Olivier Jacot-Descombes
Nov 26 '18 at 15:32
Thank you very much for the answers! Regarding question 3, you showed how we can access the same methode (differently implemented) of each class without the if or switch statment. But how to access specific properties that only class B and C are possessed?
– Sasha Vasserfirer
Nov 26 '18 at 4:46
Thank you very much for the answers! Regarding question 3, you showed how we can access the same methode (differently implemented) of each class without the if or switch statment. But how to access specific properties that only class B and C are possessed?
– Sasha Vasserfirer
Nov 26 '18 at 4:46
You have two possibilities: 1. By using these if or switch. 2. By using these properties in common methods or other properties. E.g. If the base class is a
Shape
with two derived classes Rectangle
with Length
and Width
properties and Circle
with a Radius
, then calculate the area in a common property Area
instead of accessing the new properties from outside with if or switch.– Olivier Jacot-Descombes
Nov 26 '18 at 13:53
You have two possibilities: 1. By using these if or switch. 2. By using these properties in common methods or other properties. E.g. If the base class is a
Shape
with two derived classes Rectangle
with Length
and Width
properties and Circle
with a Radius
, then calculate the area in a common property Area
instead of accessing the new properties from outside with if or switch.– Olivier Jacot-Descombes
Nov 26 '18 at 13:53
I included a better example in my answer.
– Olivier Jacot-Descombes
Nov 26 '18 at 15:32
I included a better example in my answer.
– Olivier Jacot-Descombes
Nov 26 '18 at 15:32
add a comment |
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;
}
}
1
Pattern matching is neat, but OOP was invented precisely to avoid this type of code.
– Groo
Nov 25 '18 at 21:23
Good point, actually. I just wanted to show that the values are not lost if you recover the real type from the common vehicle list.
– Waescher
Nov 25 '18 at 21:24
@Groo - "invented precisely to avoid this type of code" - What? No, it was invented to precisely allow this kind of code. Do you have a reference to avoiding it?
– Enigmativity
Nov 25 '18 at 21:39
1
@Enigmativity: I am not sure what you are referring to with "precisely allow". Polymorphism means abstracting the actual object type and hiding the actual implementation through its interface (which is violated in this example which knows specifics of each subclass). Single responsibility principle states that a class should have single reason to change (violated here, you will need to update eachswitch
/case
in your program when you add aBoat
). This code is basically the school example for refactoring into OO code.
– Groo
Nov 25 '18 at 22:15
@Groo - OOP wasn't invented with the SOLID principles. They came along later. OOP was invented to allow polymorphism. When I said "precisely allow" I was referring to your line "invented precisely to avoid this type of code".
– Enigmativity
Nov 25 '18 at 22:19
|
show 4 more comments
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;
}
}
1
Pattern matching is neat, but OOP was invented precisely to avoid this type of code.
– Groo
Nov 25 '18 at 21:23
Good point, actually. I just wanted to show that the values are not lost if you recover the real type from the common vehicle list.
– Waescher
Nov 25 '18 at 21:24
@Groo - "invented precisely to avoid this type of code" - What? No, it was invented to precisely allow this kind of code. Do you have a reference to avoiding it?
– Enigmativity
Nov 25 '18 at 21:39
1
@Enigmativity: I am not sure what you are referring to with "precisely allow". Polymorphism means abstracting the actual object type and hiding the actual implementation through its interface (which is violated in this example which knows specifics of each subclass). Single responsibility principle states that a class should have single reason to change (violated here, you will need to update eachswitch
/case
in your program when you add aBoat
). This code is basically the school example for refactoring into OO code.
– Groo
Nov 25 '18 at 22:15
@Groo - OOP wasn't invented with the SOLID principles. They came along later. OOP was invented to allow polymorphism. When I said "precisely allow" I was referring to your line "invented precisely to avoid this type of code".
– Enigmativity
Nov 25 '18 at 22:19
|
show 4 more comments
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;
}
}
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;
}
}
edited Nov 25 '18 at 21:40
answered Nov 25 '18 at 21:22
WaescherWaescher
2,85431730
2,85431730
1
Pattern matching is neat, but OOP was invented precisely to avoid this type of code.
– Groo
Nov 25 '18 at 21:23
Good point, actually. I just wanted to show that the values are not lost if you recover the real type from the common vehicle list.
– Waescher
Nov 25 '18 at 21:24
@Groo - "invented precisely to avoid this type of code" - What? No, it was invented to precisely allow this kind of code. Do you have a reference to avoiding it?
– Enigmativity
Nov 25 '18 at 21:39
1
@Enigmativity: I am not sure what you are referring to with "precisely allow". Polymorphism means abstracting the actual object type and hiding the actual implementation through its interface (which is violated in this example which knows specifics of each subclass). Single responsibility principle states that a class should have single reason to change (violated here, you will need to update eachswitch
/case
in your program when you add aBoat
). This code is basically the school example for refactoring into OO code.
– Groo
Nov 25 '18 at 22:15
@Groo - OOP wasn't invented with the SOLID principles. They came along later. OOP was invented to allow polymorphism. When I said "precisely allow" I was referring to your line "invented precisely to avoid this type of code".
– Enigmativity
Nov 25 '18 at 22:19
|
show 4 more comments
1
Pattern matching is neat, but OOP was invented precisely to avoid this type of code.
– Groo
Nov 25 '18 at 21:23
Good point, actually. I just wanted to show that the values are not lost if you recover the real type from the common vehicle list.
– Waescher
Nov 25 '18 at 21:24
@Groo - "invented precisely to avoid this type of code" - What? No, it was invented to precisely allow this kind of code. Do you have a reference to avoiding it?
– Enigmativity
Nov 25 '18 at 21:39
1
@Enigmativity: I am not sure what you are referring to with "precisely allow". Polymorphism means abstracting the actual object type and hiding the actual implementation through its interface (which is violated in this example which knows specifics of each subclass). Single responsibility principle states that a class should have single reason to change (violated here, you will need to update eachswitch
/case
in your program when you add aBoat
). This code is basically the school example for refactoring into OO code.
– Groo
Nov 25 '18 at 22:15
@Groo - OOP wasn't invented with the SOLID principles. They came along later. OOP was invented to allow polymorphism. When I said "precisely allow" I was referring to your line "invented precisely to avoid this type of code".
– Enigmativity
Nov 25 '18 at 22:19
1
1
Pattern matching is neat, but OOP was invented precisely to avoid this type of code.
– Groo
Nov 25 '18 at 21:23
Pattern matching is neat, but OOP was invented precisely to avoid this type of code.
– Groo
Nov 25 '18 at 21:23
Good point, actually. I just wanted to show that the values are not lost if you recover the real type from the common vehicle list.
– Waescher
Nov 25 '18 at 21:24
Good point, actually. I just wanted to show that the values are not lost if you recover the real type from the common vehicle list.
– Waescher
Nov 25 '18 at 21:24
@Groo - "invented precisely to avoid this type of code" - What? No, it was invented to precisely allow this kind of code. Do you have a reference to avoiding it?
– Enigmativity
Nov 25 '18 at 21:39
@Groo - "invented precisely to avoid this type of code" - What? No, it was invented to precisely allow this kind of code. Do you have a reference to avoiding it?
– Enigmativity
Nov 25 '18 at 21:39
1
1
@Enigmativity: I am not sure what you are referring to with "precisely allow". Polymorphism means abstracting the actual object type and hiding the actual implementation through its interface (which is violated in this example which knows specifics of each subclass). Single responsibility principle states that a class should have single reason to change (violated here, you will need to update each
switch
/case
in your program when you add a Boat
). This code is basically the school example for refactoring into OO code.– Groo
Nov 25 '18 at 22:15
@Enigmativity: I am not sure what you are referring to with "precisely allow". Polymorphism means abstracting the actual object type and hiding the actual implementation through its interface (which is violated in this example which knows specifics of each subclass). Single responsibility principle states that a class should have single reason to change (violated here, you will need to update each
switch
/case
in your program when you add a Boat
). This code is basically the school example for refactoring into OO code.– Groo
Nov 25 '18 at 22:15
@Groo - OOP wasn't invented with the SOLID principles. They came along later. OOP was invented to allow polymorphism. When I said "precisely allow" I was referring to your line "invented precisely to avoid this type of code".
– Enigmativity
Nov 25 '18 at 22:19
@Groo - OOP wasn't invented with the SOLID principles. They came along later. OOP was invented to allow polymorphism. When I said "precisely allow" I was referring to your line "invented precisely to avoid this type of code".
– Enigmativity
Nov 25 '18 at 22:19
|
show 4 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53471900%2fc-sharp-stacking-different-classes-in-the-same-list%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
The point of inheritance is to reuse code. The point of polymorphism is to hide the implementation from the calling code by providing a common interface. So, if you are doing proper OOP (SOLID principles are a good starting point), then placing instances of different classes into the same list only makes sense if they share the same interface. For the caller, all the object in that list will be instances of
A
. If you need to check the type of the object inside theforeach
loop, you're doing it wrong.– Groo
Nov 25 '18 at 21:20
An Interface is your friend. Anyway, if you add a
B
orC
class to aList<A>
and checkList<A>[N] is B
,List<A>[N] is C
, the test will return the actual class stored at that index. You can then cast to the right class (it may not be even necessary, depending on what you're doing with it).– Jimi
Nov 25 '18 at 21:26
2.
class A
is a Reference Type, hence only references are stored in that list (like a reference counting C++ pointer)– Dennis Kuypers
Nov 25 '18 at 23:07