-2

I'm working through Jeff Fritz's c# tutorial videos, and there is some code like this that uses an abstract class:

public abstract class Shape {}
public class Rectangle : Shape {}
public class Square : Rectangle {
    public string SquareOnlyMethod() { return "I am a square"; }
}


public static void Main()
{
    Square s = new Square(); 
    
    Console.WriteLine(s.GetType());            // Square
    Console.WriteLine(s is Shape);             // True
    Console.WriteLine(s is Rectangle);         // True
    Console.WriteLine(s is Square);            // True
    Console.WriteLine(s.SquareOnlyMethod());   // I am a square
    
    Shape r = new Square();
    
    Console.WriteLine(r.GetType());            // Square
    Console.WriteLine(r is Shape);             // True
    Console.WriteLine(r is Rectangle);         // True
    Console.WriteLine(r is Square);            // True
    Console.WriteLine(r.SquareOnlyMethod());   // 'Shape' does not contain a definition for 'SquareOnlyMethod' and no extension method 'SquareOnlyMethod' accepting a first argument of type 'Shape' could be found
}

Can somebody please explain the following?

  1. What is actually created when we do Shape r = new Square();? Is it a Shape or a Square?
  2. Why does GetType return Square but then the method cannot be found that is within the Square class?

Jeff says (if I understand correctly) that, "'Shape` is created with the footprint of Square", but then moves on.

Fiddle

EvilDr
  • 8,943
  • 14
  • 73
  • 133
  • 2
    "Is it a Shape or a Square?" It is both, because all Square objects are also Shape objects. – Joe Sewell Aug 18 '21 at 15:20
  • 1
    You create what you initialize with the `new` keyword, in this case a `Square` instance. But you assign it to a variable of it's base type `Shape`. On this way you are hiding the fact that it's a `Square`. You could always cast it back to `Square`, then you could also use `SquareOnlyMethod`. So even if it's still a `Square` the compiler doesn't allow to use methods defined in `Square` if it's declared as a `Shape` because not every `Shape` has this method. – Tim Schmelter Aug 18 '21 at 15:23

1 Answers1

1

The problem you are seeing starts with the following line Shape r = new Square(); because even though you are creating a Square but using the base type. I am assuming the concept Jeff is trying to show you is Polymorphism which can be check here with a better example of the shapes problem https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/polymorphism.

In the Microsoft example you see that the base class provides common functionality for any derive class (shape) to leverage, extend, or override. So you can potentially treat a single array of different Shape children as Shapes and access or call methods (virtual for overriding) across any child shape (hence many forms).

var shapes = new List<Shape>
{
    new Rectangle(),
    new Triangle(),
    new Circle()
};

// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (var shape in shapes)
{
    shape.Draw();
}
Felipe Ramos
  • 305
  • 3
  • 6
  • Thanks. Is this an example of "boxing" during instantiation, e.g. a Square is created but only exhibits the methods of the base class Shape? It's the GetType bit that's confusing me. – EvilDr Aug 18 '21 at 15:35
  • 1
    No, boxing happens when you need to use a value type in place of a reference type e.g. `object integer = 1`. Here, all types are reference types so no boxing occurs. – Johnathan Barclay Aug 18 '21 at 15:41
  • 1
    @EvilDr the GetType returns runtime type and since the instance type at runtime is Square - that's what you'll see. Check out https://learn.microsoft.com/en-us/dotnet/api/system.object.gettype?view=net-5.0 and https://learn.microsoft.com/en-us/dotnet/api/system.type.basetype?view=net-5.0 Those are good references for understanding the differences. – Felipe Ramos Aug 19 '21 at 15:08