Chin
Chin

Reputation: 20675

new virtual method in subclass with generics parameters constraints

I'm trying to understand a piece of code that has been simplified to the snippet below:

abstract class A_base {}
abstract class A_derived : A_base {}

abstract class B_base {
    protected virtual T Foo<T>() where T : A_base, new() {
        return new T();
    }
}

abstract class B_derived : B_base {
    protected new virtual T Foo<T>() where T : A_derived, new() {
        return base.Foo<T>();
    }
}

Specifically, I'm trying to understand the reasoning behind the new virtual in the Foo method in B_derived class. As I understand it, the author wants to tighten the generic parameter of the Foo method (from A_base to A_derived) while also letting classes derived from B_derived to override the Foo method.

There is currently no overridden implementation of Foo in any classes derived from B_base or B_derived

Are there any other reasons for doing this? Are there any pitfalls of doing this? Is this a common way to restrict the type parameter in an overridden method? Or is it a convoluted way of doing it and there are better ways?

Upvotes: 1

Views: 353

Answers (2)

Guvante
Guvante

Reputation: 19203

By saying new virtual you create a new method that shadows the original one (doesn't override the base classes version). This means any methods that are called on a class derived from B_derived which are called from a variable of type B_derived will use the new method signature (and functionality). It also means it is impossible to override the original, as it has been removed from the list of methods effectively.

To have a more concrete example I added a few classes and some logging.

abstract class A_base {}
abstract class A_derived : A_base {}

abstract class B_base {
   public virtual T Foo<T>() where T : A_base, new() {
      Console.Write("1");
      return new T();
   }
}

abstract class B_derived : B_base {
   public new virtual T Foo<T>() where T : A_derived, new() {
      Console.Write("2");
      return base.Foo<T>();
   }
}

class C1 : A_base {}
class C2 : A_derived {}

class D1 : B_base {
   public override T Foo<T>() {
      Console.Write("3");
      return base.Foo<T>();
   }

   //override T Foo<T>() where T : A_derived, new() {
   //    //Error constraint mismatch
   //}
}

class D2 : B_derived {
   public override T Foo<T>()  {
      Console.Write("4");
      return base.Foo<T>();
   }

   //override T Foo<T>() where T : A_base, new() {
   //    //Error constraint mismatch
   //}
}




public class Program
{
   public static int Main(string[] args) {
      //Comments are what is printed out.
      var d1 = new D1();
      var d2 = new D2();
      d1.Foo<C1>(); //31
      Console.WriteLine ();
      d1.Foo<C2>(); //31
      Console.WriteLine ();
      //d2.Foo<C1>(); //Error constraint mismatch
      d2.Foo<C2>(); //421
      Console.WriteLine ();
      B_base b_d2 = d2;
      b_d2.Foo<C1>(); //1
      Console.WriteLine();
      b_d2.Foo<C2>(); //1
      Console.WriteLine ();
      return 0;
   }
} 

Upvotes: 1

user3308960
user3308960

Reputation: 11

The following is the reason:

  1. [Answers use of New] Please note that Foo is not being overridden. Since overriding requires the same method signature (including constraints). Here that is not the case, the constraints are being changed, thus effectively changing the whole signature. Thus, new virtual keyword is employed.

  2. [Answers use of virtual] Now the author wants B_base.Foo() and B_derived.Foo to be treated as different methods as explained in point 1 and he also wants that user should be able to override these different methods, thus having the need to use Virtual keyword.

This is the common way of dealing the sort of problem expected when one needs to change the signature of methods and also wants to allow the ability to override each of those individual methods.

Upvotes: 1

Related Questions