最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - What's the difference between instantiating a derived object as the base type compared to instantiating it as the d

programmeradmin2浏览0评论

What is the difference in the code below between instantiating circle as type Shape and rectangle as type Rectangle?

Shape is the base class, Circle and Rectangle are both derived from Shape. Each instantiation is commented with what the VS Code debugger shows as the variable information.

I'm not sure if I'm using the right terminology; I've been learning C# for a bit over a month so forgive me if I'm not.

var shape = new Shape(); // shape = {Shape}
Shape circle = new Circle();  // circle [Shape] = {Circle}
Rectangle rectangle = new Rectangle(); // rectangle = {rectangle}

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}

What is the difference in the code below between instantiating circle as type Shape and rectangle as type Rectangle?

Shape is the base class, Circle and Rectangle are both derived from Shape. Each instantiation is commented with what the VS Code debugger shows as the variable information.

I'm not sure if I'm using the right terminology; I've been learning C# for a bit over a month so forgive me if I'm not.

var shape = new Shape(); // shape = {Shape}
Shape circle = new Circle();  // circle [Shape] = {Circle}
Rectangle rectangle = new Rectangle(); // rectangle = {rectangle}

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}
Share Improve this question asked Jan 20 at 17:02 akTedakTed 2132 silver badges9 bronze badges 6
  • 2 if you had Circle specific methods you'd have no access to them from the circle variable in the remainder of your code. You just get the Shape methods. rectangle variable gets both Rectangle methods and Shape methods. – Ivan Petrov Commented Jan 20 at 17:07
  • @IvanPetrov interesting...what would be a use case for circle having no access to Circle's public methods? Why would someone ever use something like Shape circle = new Circle()? – akTed Commented Jan 20 at 17:22
  • 1 The assignement of Circle instance to a Shape variable is called polymorphism. The third line is the same as the first // rectangle {Rectangle} = {Rectangle} – Sinatr Commented Jan 20 at 17:23
  • 1 @akTed there is no real use for this in your specific case, if you did var you'd get a Circle reference, not Shape. It might make sense if Shape was an interface and Circle implemented it via EIMIs IShape.Method()..but that's a bit too advanced I think. – Ivan Petrov Commented Jan 20 at 17:24
  • 1 There is no "real" difference; because ultimately, one can "upcast" back to the derived class. Or the "code" can; if it is allowed to. One exposes what one needs to; or wants to. At some point, one introduces "interfaces"; which can re-interpret the base and derived classes based on a particular need to know; and a "psuedo" way of achieving multiple inheritance (combining different classes). – Gerry Schmitz Commented Jan 20 at 17:26
 |  Show 1 more comment

2 Answers 2

Reset to default 3

The difference is that Circle and Rectangle are interchangeable as Shape, but Rectangle and Circle are not interchangeable on their own.

In other words you can create a typed array of Shape objects and add both Rectangle and Circle to it:

Shape[] shapes = new Shape[2];

shapes[0] = circle;
shapes[1] = rectangle;

But your Rectangle and Circle classes don't add anything, so it's not very useful.

What you're doing is called inheritance. Your Shape class is called the base class. And your Circle and Rectangle classes are called the derived classes. Inheritance allows you have:

  1. Shared behavior between objects.
  2. Override that behavior if needed in the derived classes using virtual and override methods.
  3. Add specific/unshared behavior to the derived classes.

Let's say your Shape class looked like this:

class Shape {
  public void Draw() { ... }
}

Now you can implement drawing on screen logic and call rectangle.Draw() and circle.Draw() on the array we made above:

foreach (Shape shape in shapes) {
  shape.Draw();
}

To finish it off, let's add something specific to Circle:

class Circle : Shape {
  public double Radius { get; set; }
}

A Rectangle doesn't a radius, so it makes sense to derive from Shape and add our Radius property in Circle specifically.

Note that this won't work:

foreach (Shape shape in shapes) {
  // will not compile because Radius is only defined in Circle
  var radius = shape.Radius;
}

More information: https://www.programiz.com/csharp-programming/inheritance

Let us add some "flesh" to your classes to better see the differences.

abstract class Shape
{
    public Point Location { get; set; }

    public abstract void Draw(Graphics g);
}

class Circle : Shape
{
    public int Radius { get; set; }

    public override void Draw(Graphics g)
    {
        int d = 2 * Radius;
        g.DrawEllipse(Pens.Black, Location.X - Radius, Location.Y - Radius, d, d);
    }
}

class Rectangle : Shape
{
    public int Width { get; set; }
    public int Height { get; set; }

    public override void Draw(Graphics g)
    {
        g.DrawRectangle(Pens.Black, Location.X, Location.Y, Width, Height);
    }
}

And let us also use them in a little example. I assume that we will put this code into a Window Forms Form:

private List<Shape> Shapes = new() {
    new Circle { Location = new Point(250, 250), Radius = 140 },
    new Rectangle { Location = new Point(200, 100), Width = 160, Height = 135 }
};

private void Form1_Paint(object sender, PaintEventArgs e)
{
    foreach (Shape shape in Shapes) {
        shape.Draw(e.Graphics);
    }
}

Now, to the explanation. A Shape class has no specific geometrical shape defined. It may have properties that apply to all possible shapes like border color, fill color and border width. In this example I added only a Location property.

It is also useful to add it a Draw method that will be inherited and available for all derived shapes; however, we don't know what to draw. Therefore I made the Shape class and the Draw method abstract. This means that we cannot instantiate a Shape class with new Shape().

But we can derive classes that derive from Shape. These class must implement the abstract method, unless they are abstract themselves.

The Circle class has a Radius property, the Recangle has Width and Height properties. They both implement (override) the Draw method to draw themselves.

In the usage example I have created a list of shapes. We cannot add it a Shape instance (because Shape is abstract), but Circle and Rectangle instances.

In the foreach-loop we can call the Draw method and we could also access the Location property, however we cannot access the Radius, Width and Height properties. But we don't need to, since the different Draw implementations are doing this for us. This behavior is called polymorphism and is a very powerful instrument in object oriented programming.

You asked "What is the difference [...] between instantiating circle as type Shape and rectangle as type Rectangle?". Well, you cannot instantiate a Circle as Shape, but you can assign a Circle instance to a Shape variable. "Instantiate" is what you do with the new keyword. Note that this does not change the circle in any way, but through the Shape shape variable you can see only the Shape members (properties, methods, events, etc.). The class members introduced in the derived classes are still there, but not directly accessible. This is like looking through red glasses. You see only the red colors around you, but this does not change the real colors of your surrounding.

Why should we assign a Circle or Rectangle to a Shape variable and lose access to some of its class members? It makes sense, if the Shape variable can be a Circle or a Rectangle, because then we can perform actions on it that apply to all possible shapes without having to know what shape they are (as you can see in Form_Paint).

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论