Partendo da questo post di
Marco Russo e indagando un po' sulla modifica di
accessibilità nella sovrascrittura di un metodo virtuale in una relazione di
ereditarietà, ho scoperto che, ai metodi Foo in questo codice C++:
#using <mscorlib.dll>
using namespace
System;
public __gc
class A
{
protected: virtual
void Foo()
{
Console::WriteLine(S"A");
}
};
public __gc
class B: public
A
{
protected: virtual
void Foo()
{
Console::WriteLine(S"B");
}
};
public __gc
class C: public
B
{
public: virtual
void Foo()
{
Console::WriteLine(S"C");
}
};
corrispondono le seguenti signature IL:
.method family newslot virtual instance void Foo()
cil managed //
A
.method family virtual instance void Foo() cil managed //
B
.method public virtual instance void Foo() cil managed //
C
dove si nota in rosso il flag newslot
solo sul metodo della classe base. Il flag newslot in IL
"corrisponde" al modificatore new in C# e al modificatore Shadows
in VB .NET ("corrisponde" non è, forse, la parola giusta...). Siamo
abituati a pensare a questo modificatore in situazioni di "hiding an
inherited member" (10.7.1.2, ECMA-334) e perciò si trova (di solito)
in classi derivate e non in classi base. Però, nell'esempio sopra, il
compilatore C++ l'ha inserito nella classe base!
In C# non è possibile questa modifica di accessibilità. Il seguente codice
C# non compila (vedi l'errore CS0507):
using System;
public class
A
{
protected virtual
void Foo()
{
Console.WriteLine("A");
}
}
public class
B: A
{
protected override
void Foo()
{
Console.WriteLine("B");
}
}
public class
C: B
{
// error CS0507: 'C.Foo()':
// cannot change access modifiers when
// overriding 'protected' inherited
member 'B.Foo()'
public override
void Foo()
{
Console.WriteLine("C");
}
}
Impostando però come new virtual il metodo
Foo della classe C:
using System;
public class
A
{
protected virtual
void Foo()
{
Console.WriteLine("A");
}
}
public class
B: A
{
protected override
void Foo()
{
Console.WriteLine("B");
}
}
public class
C: B
{
public new virtual void Foo()
{
Console.WriteLine("C");
}
}
lo snippet compila e otteniamo queste signature IL:
.method family hidebysig newslot virtual instance void Foo()
cil managed //
A
.method family hidebysig virtual instance void Foo() cil managed //
B
.method public hidebysig newslot virtual instance void Foo() cil managed //
C
Ma se per il metodo Foo della classe B avessimo avuto virtual
al posto di override?
using System;
public class
A
{
protected virtual
void Foo()
{
Console.WriteLine("A");
}
}
public class
B: A
{
protected virtual void Foo()
{
Console.WriteLine("B");
}
}
public class
C: B
{
public new virtual void Foo()
{
Console.WriteLine("C");
}
}
In questo caso, il compilatore C# avrebbe inserito il flag newslot nella
signature di tutti e tre i metodi:
.method family hidebysig newslot virtual instance void Foo()
cil managed //
A
.method family hidebysig newslot virtual instance void Foo() cil managed //
B
.method public hidebysig newslot virtual instance void Foo() cil managed //
C
Proprio come diceva (chi altro?) Anders Hejlsberg in questa
intervista:
"When you say "virtual," you can mean one of two things. If you did not inherit a method of the same signature, then this is a new virtual method. That's one meaning. Otherwise it is an override of an inherited method. That's the other meaning."
"In C#, you must explicitly indicate which meaning of virtual you intend. To declare a new virtual method, you just mark it
virtual. But to override an existing virtual method, you must say
override."
Avete notato? "If you did not inherit a method of the same signature, then this is a
new virtual method." e
"To declare a new virtual method, you just mark it
virtual". Cioè, un nuovo metodo virtuale significa un nuovo slot
(newslot), ovvero un nuovo elemento, nella v-table! Ecco perché ha senso
incontrare newslot in una classe base (lo snippet C++ da cui siamo
partiti). newslot non corrisponde al modificatore new ma,
lo implementa.
Ottima scelta il nome del flag in IL, newslot, non altrettanto ottima,
a mio parere, la scelta del nome del modificatore in C#/VB .NET: new/Shadows.