PeterR
PeterR

Reputation: 442

What is the use case for C# allowing to use new on a virtual method?

Being mainly a Java developer, I was a bit surprised by the result when I one day accidentally used new keyword instead of override.

It appears that the new keyword removes the "virtualness" of the method at that level in the inheritance tree, so that calling a method on an instance of child class that is downcasted to the parent class, will not resolve to the method implementation in the child class.

What are the practical use cases for this behavior?

Clarification: I understand the use of new when parent is not virtual. I'm more curious why the compiler allows new and virtual to be combined.

The following example illustrates the difference:

using System;

public class FooBar
{
    public virtual void AAA()
    {
        Console.WriteLine("FooBar:AAA");
    }

    public virtual void CCC()
    {
        Console.WriteLine("FooBar:CCC");
    }
}

public class Bar : FooBar
{
    public new void AAA()
    {
        Console.WriteLine("Bar:AAA");
    }

    public override void CCC()
    {
        Console.WriteLine("Bar:CCC");
    }
}

public class TestClass
{
    public static void Main()
    {
        FooBar a = new Bar();
        Bar b = new Bar();
        Console.WriteLine("Calling FooBar:AAA");
        a.AAA();
        Console.WriteLine("Calling FooBar:CCC");
        a.CCC();
        Console.WriteLine("Calling Bar:AAA");
        b.AAA();
        Console.WriteLine("Calling Bar:CCC");
        b.CCC();
        Console.ReadLine(); 
    }
}

This produces the following output:

Calling FooBar:AAA
FooBar:AAA
Calling FooBar:CCC
Bar:CCC
Calling Bar:AAA
Bar:AAA
Calling Bar:CCC
Bar:CCC

Upvotes: 5

Views: 795

Answers (3)

Travis Heseman
Travis Heseman

Reputation: 11449

Hypothetically....

public class BaseCollection<T>
{
  // void return - doesn't seem to care about notifying the 
  // client where the item was added; it has an IndexOf method 
  // the caller can use if wants that information
  public virtual void Add(T item)
  {
    // adds the item somewhere, doesn't say where
  }

  public int IndexOf(T item)
  {
     // tells where the item is
  }
}

public class List<T> : BaseCollection<T>
{
  // here we have an Int32 return because our List is friendly 
  // and will tell the caller where the item was added
  new public virtual int Add(T item) // <-- clearly not an override
  {
     base.Add(item);
     return base.IndexOf(item);
  }
}

Here I use the "new" modifier because a List<T> reference will hide the Add method from the BaseCollection<T>. By default, hiding members from a base generates a warning from the compiler (an error if you have compilation set up to fail on warnings). So I'm basically telling the compiler... "Yeah I know I hid the Add method with the void return, it's desired functionality - just go with it."

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1503729

Use case:

  • Today, you use a 3rd party library and derive class Banana from class Fruit.
  • You implement a method called Peel in Banana. There is no Peel in Fruit.
  • Tomorrow, the 3rd party releases a new version of the library, including a virtual Fruit.Peel method
  • You recompile your code tomorrow. Do you want to override Fruit.Peel? Quite possibly not - it could have a completely different meaning. Instead, you hide it with Banana.Peel and all the existing code works as it does today.

In other words, it's mostly to avoid versioning issues. In Java, you'd end up overriding Fruit.peel even though you didn't want to, quite possibly leading to hard-to-diagnose bugs.

Upvotes: 15

Matt
Matt

Reputation: 41842

Speaking from personal experience, I mostly see the "new" keyword used in cases where the original parent method was not specified as virtual, but an override behavior was desired. Application of the "new" keyword "hides" the parent method. And, as you observed in the code example, the method written with "new" will only be executed when working directly with that type. If working with the parent type, the parents original method will be called.

To answer your question more directly - it provides a means of overriding methods when the parent method was not labeled virtual.

EDIT: As an aside, adding the "new" keyword to hide a parent method that is not naturally overrideable does not actually change anything in the generated IL. But it is a means of explicitly stating to the developer "Hey, you're hiding a parent method here, not overriding it"

Upvotes: 2

Related Questions