Alkampfer's Place

Il blog di Gian Maria Ricci
posts - 659, comments - 871, trackbacks - 80

My Links

News

Gian Maria Ricci Mvp Logo CCSVI in Multiple Sclerosis

English Blog

Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

I miei siti

Siti utili

Quiz

No, non voglio fare concorrenza ad Adrian, ma volevo fare un piccolo quiz anche io. In realtà è una vecchissima roba che ho trovato nel mio HD, era una mia perplessità al tempo del framework 1.0, in particolare perché venivo da un lungo periodo di C++. La domanda è questa, io ho una classe Base con un metodo AMethod() che non accetta parametri. Io voglio fare una classe Derived che eredita da base, dichiara un metodo chiamato sempre AMethod() che accetta un parametro di tipo stringa. Quello che voglio è che l'utilizzatore di Derived non sia più in grado di chiamare il metodo AMethod senza parametri della classe base, ma sia sempre obbligato a chiamare la nuova versione. In sostanza voglio che il codice seguente generi errore di compilazione

class Base {

   public void AMethod() {

      Console.WriteLine("Base::AMethod");

   }

}

   

class Derived : Base {

   

   public void AMethod(String param) {

      Console.WriteLine("Base::AMethod param={0}", param);

   }

}

   

class Foo {

   static void Main() {

      Derived D = new Derived();

      D.AMethod();

      Console.ReadKey();

   }

}

Naturalmente lo snippet precedente compila e viene chiamato correttamente il metodo della classe base, la domanda è, cosa posso aggiungere alla classe Derived per fare in modo che il chiamante sia forzato a utilizzare la versione di AMethod() che accetta una stringa e non possa chiamare più la versione della classe base?

Non so se è un quiz interessante, al tempo mi richiese una mezzoretta di smattimento per trovare una soluzione e lo trovai interessante tanto da fare una discussioncina su gotdotnet, tra l'altro il codice originale era in Visual Basic :D

Alk.

P.S. Mea culpa, non avendo letto tutti i quiz di adrian, spero di non avere fatto un doppione (dato che adrian ha fatto un gran quantitativo di quiz che coprono un po' tutto) :P :P.

Print | posted on mercoledì 26 settembre 2007 13:36 | Filed Under [ .NET ]

Feedback

Gravatar

# re: Quiz

Ciao.
A prescindere che una situazione del genere la ritengo un errore, anche abb grosso di design (ovvero il nascondere in una classe derivata l'implementazione di una classe padre) ma io lo risolverei così:

class Derived : Base {

[System.Obsolete("use AMethod(String param)", true)]
public new void AMethod() {
;
}

public void AMethod(String param) {
Console.WriteLine("Base::AMethod param={0}", param);
}
}

26/09/2007 14:06 | Stefano
Gravatar

# re: Quiz

Mi associo alle considerazioni di Stefano riguardo al design.
Lo snippet che propone Stefano non penso sia la soluzione, perchè viene generato un warning e non un errore di compilazione.Io nel new void AMethod() metterei anche un throw Exception, ma anche questo non genera l'errore di compilazione ma solo a runtime
26/09/2007 14:51 | Diego Guidi
Gravatar

# re: Quiz

Beh no, la soluzione di stefano è quella giusta, un obsolete con il secondo parametro a true genera errore di compilazione.
Per quanto riguarda il design, non sono sempre daccordo. Originariamente avevo una classe di una libreria già fatta e che non potevo modificare, la classe gestiva dell'hardware, ho dovuto fare una classe più usabile perchè quella originale era cervellotica per cui ho ereditato e in alcuni metodi ho voluto togliere le vecchie chiamate sostituendo con una mia unica chiamata più chiara, però volevo che fosse chiaro per tutti che la nuova classe sostituiva quei metodi.
Esempio, considera una classe in cui per connetterti ad una seriale devi prima valorizzare tutte le proprietà, poi una openserial, poi una connect etc etc, faccio una classe ereditata che ha un metodo ConnectToSerial che è molto più user friendly, nel contempo tutta la parte di interfaccia della vecchia classe che invece è fatta bene me la tengo, altrimenti potrei fare un facade.

Per il design talvolta anche questa soluzione può servire. Ad esempio ho una classe che pilota un hardware di lettura rfid di una certa marca chiamata ad es FeigDevice, la classe base permette di comunicare tramite seriale o tcp/ip ed implemento nella classe base tutte le funzionalita di un possibile lettore di quel produttore.
A questo punto mi trovo dopo un po alcuni lettori che vanno solo in seriale oppure che supportano non tutti i comandi, è comodo a questo punto fare una ModelNumberFeigDevice che eredita dalla classe base FeigDevice e nasconde al chiamante tutti quei metodi che non sono implementati per quel particolare hardware.
Chiaramente un modo migliore di procedere è quello di fare delle interfacce che implementano i vari set, e per ogni lettore implementare le sole interfacce riguardanti i set di operazioni permesse, ma quando uno ha già una classe fatta e non ha tempo di fare refactoring la possibilità di nascondere dei metodi del padre è utile.
Considerate che in C++ di default se si fa overload di un metodo presente nel padre a tutti gli effetti i metodi del padre con lo stesso nome non sono accessibili.Inoltre in C++ ho l'ereditarietà privata che potrebbe essere una soluzione ancora migliore per questi casi.

Alk.
26/09/2007 16:25 | Gian Maria
Gravatar

# re: Quiz

Una precisazione: a livello di design una situazione del genere è ammissibile solamente quando non si usa polimorfismo, in pratica quando io utilizzo la classe D e non faccio mai un cast alla classe base.
Nel mio caso era proprio cosi, perché andavo a creare una classe derivata che a livello logico costituiva un Facade del gestore hardware originario, ma il chiamante non doveva nemmeno conoscere l'esistenza del gestore originario ed usare solo la mia classe.
Sostanzialmente quando l'utilizzatore è a conoscenza della classe base, nascondere i metodi in questo modo è decisamente fuorviante anche perchè nessuno mi vieta di fare.

Derived D = new Derived();
((Base) D).AMethod();

e chiamare comunque il metodo della classe base. In sostanza quello che si vuole fare è un po il "is-implemented-in-term-of". Chiaramente avendo l'ereditarietà come in C++ privata si sarebbe potuto procedere in maniera formalmente più corretta ovvero: Ereditare privatamente e riesporre al chiamante le sole funzionalità del facade, in questo caso il chiamante non può accedere all'interfaccia della classe base e quindi non c'è confusione.

Alk.
26/09/2007 16:37 | Gian Maria
Gravatar

# re: Quiz

se a qualcuno la discussione può interessare ho aperto un thread su guisa http://www.guisa.it/forums/1328/ShowThread.aspx#1328
26/09/2007 16:39 | Gian Maria
Gravatar

# re: Quiz

"Beh no, la soluzione di stefano è quella giusta, un obsolete con il secondo parametro a true genera errore di compilazione."
Ah vero, nn mi ricordavo :(

26/09/2007 17:17 | Diego Guidi
Gravatar

# re: Quiz

Mentre nell'originale in VB é ancora più pulito! ;-)
Basta dichiarare il metodo come private e Shadows (http://msdn2.microsoft.com/it-it/library/1h3wytf6(VS.80).aspx)
Private Shadows Sub AMethod()
Il metodo non comparirà nemmeno nella lista dei metodi dell'Intellisense
26/09/2007 17:40 | Michele Bernardi
Gravatar

# re: Quiz

Bene michele :D aspettavo anche questo, uno dei casi in cui vb ha un comportamento che mi piace un po di più del c# :D, non c'è nemmeno bisogno di dichiarare il metodo privato, basta fare il nuovo metodo e dichiararlo shadows

Public Class base
Public Function AMethod() As String
Return "Amethod"
End Function
End Class

Public Class Derived : Inherits base

Public Shadows Function AMethod(ByVal param As String) As String
Return "amethod " + param
End Function

End Class

Sub Main()

Dim d As New Derived
d.AMethod()
Console.WriteLine(DirectCast(d, base).amethod())
End Sub

In questo caso d.Amethod() da errore perchè avere ridefinito come shadows la funzione Amethod con un parametro ha automaticamente nascosto la classe base. LA differenza è che il new in c# emette comunque in msil un hidebysig che in pratica significa che la funzione nasconde solamente le versioni della classe base con la stessa signature. il vb non emette hidebysig e quindi ecco che si comporta in un modo che a me piace un po di più.

Alk.
26/09/2007 20:16 | Gian Maria
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET