Reputation: 20675
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
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
Reputation: 11
The following is the reason:
[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.
[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