Newsgroup: it.comp.lang.delphi
Titolo: [OOP] Tipizzare una TList ereditata

Ciao a tutti,
discutendo in un post di ieri, "Classe o puntatore", a riguardo
della TList, mi è sorto un dubbio su del codice che ho scritto
per tipizzare una TMyObjList, ereditata da TList.
Questa mia nuova Lista (TMyObjList) dovrebbe gestire, con le 
stesse modalità della TList, invece di Pointer generici, un tipo 
ben preciso di classi.
I miei dubbi sono al riguardo delle clausole reintroduce; virtual; 
che uso per poter reimplementare i metodi della TList genitore.
Il problema sta nel fatto che, i metodi di TList di cui voglio cambiare 
il comportamento non hanno la direttiva virtual e quindi non posso 
farne l'override nella classe ereditata.
Secondo voi sono corrette le direttive che uso nella dichiarazione
della classe ereditata?
In secondo luogo è corretto il controllo che faccio per capire 
se l'istanza della classe passata ai metodi è del tipo da me voluto,
o esiste qualcosa di più efficace, utile, efficiente ...?
Ho provato anche usando l'operatore IS ma non mi da errore
se al metodo passo un oggetto di classe diversa con il casting
//In esecuzione non mi da l'eccezione, invece vorrei che la desse
MyObjList.Add(TMyObj(TLabel.Create(nil)));
Posto il codice dopo i saluti.
Grazie ...
	Ciao
	MassimoB
------------------------------------------------------------------------------
unit UTypedList;
interface
uses
 Classes, StdCtrls;
Const
  SListTypeError
     = 'Invalid Object type at index %d';
type
  //Classe di esempio
  TMyObj = class{...}
    {...};
  End;
  TMyObjList = class(TList)
  protected
    function Get(Index: Integer): TMyObj;
                Reintroduce; virtual;
    procedure Put(Index: Integer;
                  Item: TMyObj);
                Reintroduce; virtual;
  public
    function Add(Item: TMyObj): Integer;
                Reintroduce; virtual;
    function Extract(Item: TMyObj): TMyObj;
                Reintroduce; virtual;
    function First: TMyObj;
                Reintroduce; virtual;
    function IndexOf(Item: TMyObj): Integer;
                Reintroduce; virtual;
    procedure Insert(Index: Integer;
                     Item: TMyObj);
                Reintroduce; virtual;
    function Last: TMyObj;
                Reintroduce; virtual;
    function Remove(Item: TMyObj): Integer;
                Reintroduce; virtual;
    property Items[Index: Integer]: TMyObj
                read Get write Put;  default;
  end;
implementation
{ TMyObjList }
function TMyObjList.Add(Item: TMyObj): Integer;
begin
  Result := -1;
  If Item.ClassName<>TMyObj.ClassName Then
    Error(SListTypeError,-1)
  Else
    Result := inherited Add(Item);
end;
function TMyObjList.Extract(Item: TMyObj): TMyObj;
begin
  Result := nil;
  If Item.ClassName<>TMyObj.ClassName Then
    Error(SListTypeError,-1)
  Else
    Result := TMyObj(inherited Extract(Item));
end;
function TMyObjList.First: TMyObj;
begin
  Result := TMyObj(Inherited First);
end;
function TMyObjList.Get(Index: Integer):TMyObj;
begin
  Result := TMyObj(inherited Get(Index));
end;
function TMyObjList.IndexOf(Item:TMyObj):Integer;
begin
  Result := -1;
  If Item.ClassName<>TMyObj.ClassName Then
    Error(SListTypeError,Result)
  Else
    Result := inherited IndexOf(Item);
end;
procedure TMyObjList.Insert(Index: Integer;
                            Item: TMyObj);
begin
  If Item.ClassName<>TMyObj.ClassName Then
    Error(SListTypeError,Index)
  Else
    inherited Insert(Index,Item);
end;
function TMyObjList.Last: TMyObj;
begin
  Result := TMyObj(Inherited Last);
end;
procedure TMyObjList.Put(Index: Integer;
                         Item: TMyObj);
begin
  If Item.ClassName<>TMyObj.ClassName Then
    Error(SListTypeError,Index)
  Else
    inherited Put(Index,Item);
end;
function TMyObjList.Remove(Item:TMyObj):Integer;
begin
  Result := -1;
  If Item.ClassName<>TMyObj.ClassName Then
    Error(SListTypeError,Result)
  Else
    Result := inherited Remove(Item);
end;
end.

Personalmente, uso un modo diverso per gestire casi del genere, una cosa 
di questo tipo:
type
   TMyListItem = class
   public
     property MyValue: integer read GetMyValue write setMyValue;
   end;
 
   TMyList = class
   private
      FList: TList;
      procedure FreeContainedItems;
   public
      property Count: integer read getCount;
      function  Add(AItem: TMyListItem): integer;
      property item[const AIdx: integer]: TMyListItem  read GetItem 
write setItem;
      constructor Create;
      destructor Destroy; override;
   end;
function TMyList.GetCount: integer;
begin
   Result := List.Count;
end;
function TMyList.Add(AItem: TMyListItem): integer;
begin
   Result := FList.Add(AItem)
end;
constructor TMyList.CReate;
begin
   inherited Create;
   FList := TList.Create;
end;
destructor TMyList.Destroy ;
begin
   FreeContainedItems;
   FList.Free;
   inherited;
end;
procedure TMyList.FreeContainedItems;
var
   i: integer;
begin
   for i:= pred(FList.count) downto 0 do
      TMyListItem(FList.Items[i]).Free;
end;
function TMyList.GetItem(const AIdx: integer): TMyListItem;
begin
   Result := Nil;
   if isValidIndex(AIdx) then
     Result := TMyListItem(FList.Items[i])
   else
     raise exception.Create(format('index %d is out of bound (0..%d', 
[aIfx, pred(FList.count)]))
end;
Imho, in questo modo hai:
1) una tipizzazione molto forte bypassando il problema che qualcuno 
possa aggiungere alla lista un "qualcosa" di non compatibile.
2) un alto livello di information hiding.
A  te non frega niente sapere *come* fa la tua classe a mantenere la 
lista degli elementi: ti basta che lo faccia. Se un giorno decidi che la 
tlist non ti sta bene, cambi solo la classe ma non il resto della app.
Ad esempio, potresti usare una tthreadlist....
Bye
A

 2) un alto livello di information hiding
Aggiungerei come 3 punto che il metodo che usi forse è la soluzione più
indicata, in quanto TList non è nata per essere estesa attraverso
l'ereditarietà, come invece è per le classi TStrings e TStringList per
esempio o altre sue estensioni, quindi l'approccio per composizione che
usi tu, Alberto, secondo me è di gran lunga preferibile.
Antonio

 l'ereditarietà,
Che vuoi dire ? TList viene regolarmente ereditata, all'interno della
VCL (vedi TObjectList, TComponentList, etc..).

 ben preciso di classi.
Posso chiederti perché non usi una TObjectList ?!

Posso chiederti perché non usi una TObjectList ?!
non c'è nessun motivo ... non avevo bisogno di un free
degli oggetti con la cancellazione deol riferimento dalla lista
e quindi ho usato una semplice TList ... se evessi avuto
bisogno di una gestione di memoria degli oggetti l'avrei 
usata ;)
	Ciao
	MassimoB
 

VCL (vedi TObjectList, TComponentList, etc..).
grazie al tuo post sono andato a vedere l'implementazione
di TObjectList e mi sono accorto che le direttive
reintroduce; Virtual; con cui marco i metodi da sovrascrivere
possono essere anche evitate ... ovviamente il virtual no se poi
in futuro vorrò chiamare un override in ipotetiche classi ereditate.
	Ciao
	MassimoB
 

 VCL (vedi TObjectList, TComponentList, etc..).
Intendevo dire che non è nata con l'intento di essere ereditata
polimorficamente, ovvero con l'intenzione di Massimo. Sono stato
"impreciso" ;-).
Antonio

A
Grazie
	Ciao
	MassimoB
 

 usata ;)
La TObjectList libera la memoria degli oggetti puntati solo se le
viene esplicitamente richiesto di farlo, mediante la proprietą
"OwnsObjects" (che č a True per default).

"OwnsObjects" (che è a True per default).
l'avevo trovato 5 min fa :)
userò un TObjectList come tra l'altro consigliavo
nel Thread di ieri "Classe o puntatore" da cui o preso spunto.
Non seguire i propri consigli è la cosa più stupida 
che si possa fare :) 
Grazie
	Ciao
	MassimoB
 

 ma una crescita di entrambi ...
condivido&sottoscrivo.
E' fondamentale avere punti di vista diversi... vedi il punto 1.
A

E' fondamentale avere punti di vista diversi... 
:)
	Ciao
	MassimoB
 

userò un TObjectList come tra l'altro consigliavo
[...]
MassimoB ti vedo in forma ultimamente... :-)
^___^
-- 
Morde. 
(
mo...@despammed.com
)
M-List di icld: 
http://it.groups.yahoo.com/group/icld/

Morde. 
p.s.: ma il tuo post era sarcastico ... 
ho detto qualche stupidaggine? :)
	Ciao
	MassimoB
 

p.s.: ma il tuo post era sarcastico ... 
Non era sarcastico! 
Ci ho messo gli smiles apposta ma non hanno funzionato: ti vedo molto attivo
utlimamente, e con argomenti assai interessanti quanto tecnici.
tutto li', era un complimento :)
Ciao!


Morde. 
	Ciao
	MassimoB