Il polimorfismo e le interfaccie sono senza dubbio una degli aspetti più interssanti della programmazione OOP... sebbene spesso vogliamo dare ai comportamenti di una classe e/o di un interfaccia degli aspetti quasi umani ci dobbiamo convincere che quelli che noi chiamiamo comportamenti sono contratti definiti da tanto di vTable e tecnicismi correlati ....questo per dire che non è sufficiente che una classe abbia per nome e firma gli stessi comportamenti definiti da un interfaccia per essere castabile in quell'interfaccia... la classe deve comunque avere nella sua definizione (nel suo contratto) quell'interfaccia... per alcuni questa affermazione può sembrare banale... quasi scontata, ma con i generics - in alcune casistiche - ecco che l'ovvietà di cui sopra diventa una vera e propria scocciatura! (?)
Guardiamo l'esempio qui sotto illustrato. B deriva da A ma l'enumeratore genrico tipizzato di B sebbene ha intrinsecamente il comportamento di un enumeratore generico tipizzato su A... i due enumeratori non hanno nulla a che spartirsi se non l'origine comune!
Se come San Tomasso volessi fare le prove usando "Fruit" (A) e "Apple" (B) definiti nel post "Come ti sommo le pere con le mele" potremo facilmente constatare che il casting non è possibile... ne implicito, bloccato da un ferreo odioso ma corretto compilatore ...
e neppune è possibile un casting esplicito... bloccato - ancor peggio - a runtime
Come potremmo uscire?! Beh se l'oggetto non è castabile ma ha il comportamente equiparabile possiamo giocarcela con la trasformazione (un enumetaore di Apple è convertibile in enumeratore di Frutti)... per cui ecco una soluzione che fa uso dell'incapsulamento...
Definisco un enumerator generico come quello che segue
class Enumerator<T, K>: IEnumerator<T> where K :T
{
IEnumerator<K> enumerator;
public Enumerator(IEnumerable<K> enumerable)
{
this.enumerator = enumerable.GetEnumerator();
}
#region IEnumerator<T> Members
T IEnumerator<T>.Current
{
get{return this.enumerator.Current;}
}
#endregion
#region IDisposable Members
void IDisposable.Dispose()
{
this.enumerator.Dispose();
}
#endregion
#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get { return this.enumerator.Current; }
}
bool System.Collections.IEnumerator.MoveNext()
{
return this.enumerator.MoveNext();
}
void System.Collections.IEnumerator.Reset()
{
this.enumerator.Reset();
}
#endregion
}
...che applicato al nostro caso suona così:
IEnumerator enumerator;
IEnumerator<Fruit> enumFruits = fruits.GetEnumerator();
IEnumerator<Apple> enumApples = apples.GetEnumerator();
enumerator = enumApples;
//enumFruits = (IEnumerator<Fruit>)enumApples;
enumFruits = new Enumerator<Fruit, Apple>(apples);
Se qualcuno pensa che questa complicazione sia solo odiosa burocrazia... ecco cosa si potrebbe scrivere se non ci fosse impedito!
Collection<Pear> pears = new Collection<Pear>();
// Sarebbe possibile aggiungere mele in cestino tipizzato per SOLE pere!!
((Collection<Fruit>) pears).Add(new Apple());
Nel prossimo post vedo di mostrare come ho usato l'enumeratore qui definito per dare una soluzione più OOP a "Come ti sommo le pere con le mele".
posted @ giovedì 18 maggio 2006 01:18