Web Log di Adrian Florea

"You know you've achieved perfection in design, not when you have nothing more to add, but when you have nothing more to take away." Antoine de Saint-Exupery
posts - 440, comments - 2715, trackbacks - 3944

My Links

Archives

Post Categories

Image Galleries

.RO Blogs

.RO People

.RO Sites

Blogs

Furls

Links

vinCitori

Qualcosa di nuovo sul new

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.

Print | posted on mercoledì 9 febbraio 2005 13:53 | Filed Under [ Carillon .NET ]

Powered by:
Powered By Subtext Powered By ASP.NET