DEV Community

dhavalraj007
dhavalraj007

Posted on

Override and New confusion in CSharp

Try to read this answer from SO first https://stackoverflow.com/a/11046733/15225066
If you understand that. no need to read this article.

I will not provide the formal definitions and shit. If you want that go to MSDN.

Overview

  1. Some Basics
  2. Some Basics about "new" modifier
  3. Example 1
  4. Example 2 (do it yourself)

Some Basics

Objects can have different blueprint at runtime when using inheritance.
First thing You need to understand is that There is difference between object reference and the actual object.

    class Animal{/// Some code}
    class Cow : Animal{///Some code}

    static void Main()
    {
        Cow c = new Cow();
        // Note that we can also assign the Cow object to a Animal refrence
        Animal an = new Cow();
    }
Enter fullscreen mode Exit fullscreen mode

(( Note that this new keyword is different. ))
new Cow() creates a object in heap returns its refrence(pointer or address of object in heap)
and we assign that to c variable which is refrence to a object of type of Cow.
Cow c, Animal an = Object reference
new Cow() = Actual Object

Main Point

The runtime blueprint(Methods and member vars) belongs to a "Actual Object" not to refrence. whereas the Access to Blueprint is determined by the Object reference. (read this line again this is very important)
meaning the Methods and member vars that the object has, gets decided when the object is created. and to access those methods we need the refrence which limits the access to those methods.

    class Animal
    {
        public virtual void Walk() { }
    }
    class Cow : Animal
    {
        public override void Walk() { }
        public void Milk() { }
    }

    static void Main()
    {
        Cow c = new Cow();

        // Note that I can also assign the Cow object to a Animal refrence
        Animal an = new Cow();
    }

Enter fullscreen mode Exit fullscreen mode

Image description

  • Object
    • new Cow()
    • new Cow()
    • both are same
  • methods in Object new Cow()
    • void Cow.Milk()
    • void Cow.Walk()
  • methods accesible with refrence Animal an = new Cow()
    • void Cow.Walk() (which is the overriden method)
  • methods accesible with refrence Cow c = new Cow()
    • void Cow.Walk() (which is the overriden method)
    • void Cow.Milk()

(1. Actual Object)
As you can see both an and c has same type of methods.

(2. Object Refrence)
only the refrence that you use to access Cow() object limits the access to methods.
With c you can access void Cow.Milk() and void Cow.Walk()
but with an you can access void Cow.Walk() only.(which is the overriden method)

(1. Actual Object )
In this example above, when we create the object new Cow(), the methods it has are Cow.Milk() and virtual void Animal.Walk() but this Walk method is overriden by override void Cow.Walk() thus it has only to methods Cow.Milk() and Cow.Walk(). essentially when using override it replaces the virtual method.
(2. Object Refrence)
now based on what refrence you use for this object new Cow() that will determine what methods you can access. if you use Animal an reference then you can only use Cow.Walk() because the Animal refrence only has access to Animal.Walk Method which was replaced by Cow.Walk(). Thus an.Walk() will resolve to Cow.Walk(). where as Cow.Milk() exists on object but can not be called by Animal an refrence.

In general, the child reference has access to all the parent methods.

If you still didn't get the point you can experiment it yourself with inheritance and to examine the runtime behaviour you can use System.Refection ( I find it helpful to use in VS debugger Watch window as you can see in above Screengrab).

Some Basics about "new" modifier

new modifier creates the new method with same signature as inherited method. it has no connection no inheritance.
consider new methods as methods which only allow the class in which it was declared to have access.
Look at the following code similar to the above case.

    class Animal
    {
        public virtual void Walk() { }     //virtual here is optional
    }
    class Cow : Animal
    {
        public new void Walk() { }             // new here is also optional
        public void Milk() { }
    }

    static void Main()
    {
        Cow c = new Cow();

        // Note that I can also assign the Cow object to a Animal refrence
        Animal an = new Cow();

        c.Walk();       // calls Cow.Walk()
        an.Walk();      // calls Animal.Walk()
    }

Enter fullscreen mode Exit fullscreen mode

Image description

  • Object
    • new Cow()
    • new Cow()
    • both are same
  • methods in Object new Cow()
    • void Cow.Milk()
    • void Cow.Walk()
    • void Animal.Walk()
  • methods accesible with refrence Animal an = new Cow()
    • void Animal.Walk()
  • methods accesible with refrence Cow c = new Cow()
    • void Cow.Walk() (which is the new method)
    • void Cow.Milk()

(1. Actual Object)
similar to previous case both new Cow() objects contain same methods.
But in this case we have two Walk() methods in the object. (and one Milk() method)
One of them is Animal.Walk() and other is Cow.Walk() .
So when we used new instead of override it did not replace the base class method instead it created a new method with the same signature in the derived class.

Now that we know what all methods the object contain. we can determine the access rules by using refrence types.

(2. Object Reference)
The Object new Cow() contains two similar methods Animal.Walk() ,Cow.Walk().
when we call an.Walk() because the Animal an refrence has no access to any methods that its derived classes contains (general rule of inheritance). an.Walk() resolves to Animal.Walk().
When we call c.Walk() it resolves to Cow.Walk(). because the Cow c refrence has access to all methods of Animal and Cow (meaning access to all to three methods.). because we used new on Cow.Walk() it shadows the Animal.Walk() And thus call to c.Walk() resolves to Cow.Walk().

Example 1

using System;
using System.Reflection;

public class NewVsOverride
{
    public class Base
    {
        public virtual void Method1()
        {
            Console.WriteLine("Base - Method1");
        }

        public virtual void Method2()
        {
            Console.WriteLine("Base - Method2");
        }
    }

    public class Derived:Base
    {
        public override void Method1()
        {
            Console.WriteLine("Derived - Method1");
        }

        public new void Method2()
        {
            Console.WriteLine("Derived - Method2");
        }
    }

    public static void test()
    {
        Base bc = new Base();
        bc.Method1();  
        bc.Method2();

        Derived dc = new Derived();
        dc.Method1();
        dc.Method2();

        Base bcdc = new Derived();
        bcdc.Method1();
        bcdc.Method2();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Object and Methods they contain. Base() | Derived() | Derived() ------------ | --- | --- Base.Method1() | (overriden)Derived.Method1()| (overriden)Derived.Method1() Base.Method2() | (Shadowed in Derived) Base.Method2()| (Shadowed in Derived) Base.Method2() -- | (new) Derived.Method2() | (new) Derived.Method2()
  • Refrences and Methods they can access bc | dc | bcdc ------------ | --- | --- bc.Method1() -> Base.Method1() | dc.Method1() -> (overriden)Derived.Method1()| bcdc.Method1() -> (overriden)Derived.Method1() bc.Method2() -> Base.Method2() | (Shadowed) Base.Method2() ( not accesible with Derived refrence)| bcdc.Method2() -> Base.Method2() (Shadowed in derived class not in base class.) -- | dc.Method2()-> (new) Derived.Method2() | (new) Derived.Method2() (Base ref has no access to child)

Example 2 (do it yourself)

Similarly try to understand the following example.
when calling test() from main.

using System;
using System.Reflection;
public class NewVsOverride2
{
    public class Base
    {
        public virtual void Method1()
        {
            Console.WriteLine("Base - Method1");
            Method2();  // will call Base.Method2 because Base.Method1 has no access to Shadowed new Derived.Method2
        }

        public virtual void Method2()
        {
            Console.WriteLine("Base - Method2");
        }
    }

    public class Derived : Base
    {
        //comment Derived.Method1 for test2
        public override void Method1()
        {
            Console.WriteLine("Derived - Method1");
            Method2(); //will call Derived.Method2 because In Derived Method2 is shadowed 
        }

        public new void Method2()
        {
            Console.WriteLine("Derived - Method2");
        }
    }

    public class Derived2 : Derived
    {
        public override void Method1()
        {
            Console.WriteLine("Derived2 - Method1");
        }

        public new void Method2()
        {
            Console.WriteLine("Derived2 - Method2");
        }
    }

    public static void test()
    {
        test0();

        Console.WriteLine();
        test1();
        Console.WriteLine();

        test2();

    }

    public static void test0()
    {
        Base bc = new Base();
        bc.Method1();
        bc.Method2();

        Derived dc = new Derived();
        dc.Method1();
        dc.Method2();

        Base bcdc = new Derived();
        bcdc.Method1();
        bcdc.Method2();

        Derived2 d2 = new Derived2();
        d2.Method1();
        d2.Method2();
    }

    public static void test1()
    {
        Derived dc = new Derived();
        dc.Method1();
        Base bcdc = dc;
        bcdc.Method1();
        dc.Method2();
    }

    //comment Derived.Method1 for test2
    public static void test2()
    {
        Derived dc = new Derived();
        dc.Method1();
        Base bcdc = dc;
        bcdc.Method1();
        dc.Method2();
    }
}
Enter fullscreen mode Exit fullscreen mode

Note: for test2 comment Derived.Method1
Output:

//test0
Base - Method1
Base - Method2
Base - Method2
Derived - Method1
Derived - Method2
Derived - Method2
Derived - Method1
Derived - Method2
Base - Method2
Derived2 - Method1
Derived2 - Method2
Enter fullscreen mode Exit fullscreen mode
//test1
Derived - Method1
Derived - Method2
Derived - Method1
Derived - Method2
Derived - Method2
Enter fullscreen mode Exit fullscreen mode
//test2
Base - Method1
Base - Method2
Base - Method1
Base - Method2
Derived - Method2
Enter fullscreen mode Exit fullscreen mode

if you understood outputs for test1() and test2() . I think that will be it.

Refrences:
https://stackoverflow.com/a/11046733/15225066
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords
https://stackoverflow.com/questions/17717570/why-does-calling-a-method-in-my-derived-class-call-the-base-class-method
https://stackoverflow.com/questions/11046387/exact-difference-between-overriding-and-hiding

Top comments (0)