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

Sharp-lock Holmes (il vincitore e la soluzione)

Sotto questa riga, l'eccellente soluzione di Claudio Brotto al test "Sharp-lock Holmes". Molto bene ha condotto il ragionamento, vero? Ed eccolo inserito nel blogroll :-)


Foo.dll è un assembly .NET che definisce, almeno, i tipi A, B e C. Potrebbe essere stato generato da un qualsiasi compilatore conforme a CLS, così come da ilasm a partire da codice IL.

Essendo referenziati senza qualificatore di namespace, A, B e C devono appartenere allo stesso namespace in cui è definita D (oppure deve essere presente in precedenza una direttiva using).

A, B e C possono essere classi o interfacce. Poichè le specifiche C# impongono che, in una lista di derivazione contenente più tipi, prima venga messa la classe da cui si eredita (l'ereditarietà multipla non è supportata), quindi le interfacce che si implementano, A è una classe e B e C sono interfacce. Inoltre, A, B e C devono avere visibilità public, altrimenti non sarebbero referenziabili da un assembly esterno, quello di D, appunto.

Non avendo definito esplicitamente il costuttore di D, il compilatore C# provvede ad inserirne uno di default come segue:

public D(): base() {}

Questo implica che il costruttore di A sia, almeno, protected. Dal momento che a video viene stampata la stringa "Ciao!", sarà a questo punto il costruttore di A (o un metodo che questo chiama) ad invocare la Console.WriteLine(...).

Per quanto riguarda le interfacce e la catena di ereditarietà, il discorso è un po' più lungo. D non fa l'override di alcun metodo. Se nè B nè C definiscono metodi, no problem. Se invece ne definiscono, questo influenza A. Se A, o un suo antenato nella catena di ereditarietà, implementano gli eventuali metodi dell'interfaccia B (per C il discorso è analogo) in modo implicito, cioè ad esempio

public interface B
{
  void Something() {}
}

public class A: (B)
{
  public (virtual) void Something() {}
}

allora D ne eredita l'implementazione. Le chiamate a tale metodo, indipendentemente dal tipo di riferimento sul quale sono effettuate, seguono il consueto dispatch per i metodi virtuali.

Da notare che il compilatore C# non impone che un metodo di implementazione di un interfaccia sia dichiarato virtual, salvo poi inserire lui i modificatori newslot e virtual a livello di metadati della classe base.

Da notare inoltre che non è necessario che A implementi B, dal momento che già D lo fa (e l'implementazione che fornisce è quella di A). Nel caso, invece, A implementi B, l'aver incluso B nella catena di derivazione di D è superfluo.

In sunto: l'unione dei metodi definiti da B e C deve avere una corrispondente implementazione in A o in una classe da cui A deriva. Se l'unione è vuota (B e C non definiscono metodi) A e la sua catena di derivazione possono non definire metodi ... a parte quelli di Object, ovviamente.

L'esempio più breve per il codice di foo.dll (in C#) potrebbe essere questo:

public class A
{
  protected A() {System.Console.WriteLine("Ciao");}
}

public interface B {}
public interface C {}

Voila!

Print | posted on sabato 12 marzo 2005 14:21 | Filed Under [ Test Sharp ]

Powered by:
Powered By Subtext Powered By ASP.NET