2013-04-25 32 views
13

Spodziewam się, że liczenie odwołań powinno działać na zewnętrznym obiekcie agregującym w implementacji interfejsu. Jeśli mogę odnosić się do innego przykładu: Clarity in classes implementing multiple interfaces (alternative to delegation):Interfejs Delphi implementuje

Oto minimalne odwzorowanie zachowania:

program SO16210993; 

{$APPTYPE CONSOLE} 

type 
    IFoo = interface 
    procedure Foo; 
    end; 

    TFooImpl = class(TInterfacedObject, IFoo) 
    procedure Foo; 
    end; 

    TContainer = class(TInterfacedObject, IFoo) 
    private 
    FFoo: IFoo; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    property Foo: IFoo read FFoo implements IFoo; 
    end; 

procedure TFooImpl.Foo; 
begin 
    Writeln('TFooImpl.Foo called'); 
end; 

constructor TContainer.Create; 
begin 
    inherited; 
    FFoo := TFooImpl.Create; 
end; 

destructor TContainer.Destroy; 
begin 
    Writeln('TContainer.Destroy called');//this line never runs 
    inherited; 
end; 

procedure Main; 
var 
    Foo : IFoo; 
begin 
    Foo := TContainer.Create; 
    Foo.Foo; 
end; 

begin 
    Main; 
    Readln; 
end. 

Jeśli zamiast używać implements, zaimplementować interfejs w klasie TImplementor następnie biegnie destructor.

+5

"Czy coś pomijam?" Nie wiem Ale na pewno jesteśmy. Zapomniałeś podać kod! Wymagany jest pełny program demonstrujący zachowanie. W przeciwnym razie musimy zgadywać. –

+1

masz dodatkowe referencje lub pętle referencyjne. Dodaj przesłonięcia dla TFirstSecond._AddRef i TFirstSecond._Release i umieść tam punkty przerwania, uzyskaj pełną listę odnośników i zobacz, które z nich nie zostały wyczyszczone. –

+0

Problem polega na tym, że twoje interfejsy są delegowane. Nie wiem, dlaczego to powoduje to zachowanie. –

Odpowiedz

15

To, co się tutaj dzieje, to wywołanie TContainer.Create i utworzenie instancji obiektu. Ale potem przypisujesz tę instancję do referencji interfejsu, globalnej zmiennej Foo. Ponieważ zmienna ta jest typu IFoo, delegowanie interfejsu oznacza, że ​​obiektem implementującym jest instancja TFooImpl i nie z instancją TContainer.

W związku z tym nic nie ma odniesienia do instancji TContainer, jej licznik referencji nigdy nie jest zwiększany, a więc nigdy nie jest niszczony.

Nie sądzę, aby było to bardzo łatwe. Możesz używać TAggregatedObject, ale może to nie rozwiązać Twojego problemu. Zmusiłoby to do zadeklarowania, że ​​TContainer.FFoo ma być typu TFooImpl, co uważam, że nie chcesz robić. Tak czy inaczej, oto jak to wygląda przeredagowanej ten sposób:

program SO16210993_TAggregatedObject; 

{$APPTYPE CONSOLE} 

type 
    IFoo = interface 
    procedure Foo; 
    end; 

    TFooImpl = class(TAggregatedObject, IFoo) 
    procedure Foo; 
    end; 

    TContainer = class(TInterfacedObject, IFoo) 
    private 
    FFoo: TFooImpl; 
    function GetFoo: IFoo; 
    public 
    destructor Destroy; override; 
    property Foo: IFoo read GetFoo implements IFoo; 
    end; 

procedure TFooImpl.Foo; 
begin 
    Writeln('TFooImpl.Foo called'); 
end; 

destructor TContainer.Destroy; 
begin 
    Writeln('TContainer.Destroy called');//this line does run 
    FFoo.Free; 
    inherited; 
end; 

function TContainer.GetFoo: IFoo; 
begin 
    if not Assigned(FFoo) then 
    FFoo := TFooImpl.Create(Self); 
    Result := FFoo; 
end; 

procedure Main; 
var 
    Foo : IFoo; 
begin 
    Foo := TContainer.Create; 
    Foo.Foo; 
end; 

begin 
    Main; 
    Readln; 
end. 

documentation nie mówi na ten temat:

Klasa użyć, aby implementować interfejs delegowane powinny pochodzić z TAggregationObject.

Początkowo nie mogłem znaleźć żadnej dokumentacji dla tego TAggregationObject. I wreszcie zdałem sobie sprawę, że faktycznie ma on nazwę TAggregatedObject i jest to documented.

TAggregatedObject zapewnia funkcjonalność dla wewnętrznej przedmiotu w agregatu poprzez wdrożenie metod IInterface powierzyć sterującego IInterface The.

Zagregowany obiekt to obiekt złożony z kilku połączonych obiektów . Każdy obiekt implementuje własne zachowanie i interfejsy, ale wszystkie obiekty mają tę samą liczbę odniesienia, która jest obiektem obiektu kontrolera . W układzie pojemnika kontrolerem jest obiekt kontenerowy .

Obiekt TAggregatedObject sam nie obsługuje żadnych interfejsów. Jednakże, jako typowe dla agregatu, implementuje on metody IInterface, które są używane przez obiekty, które z niego się wywodzą. Obiekt TAggregatedObject służy jako baza dla klas, które implementują interfejsy do tworzenia obiektów, które są częścią agregatu .

Obiekt TAggregatedObject służy jako baza dla klas tworzących obiekty i łączących obiekty.Korzystanie z obiektu TAggregatedObject zapewnia, że ​​wywołania do metod interfejsu IInterfejsu delegują do kontrolera IInterface agregatu.

Kontrolny interfejs II jest określony w konstruktorze dla TAggregatedObject i jest wskazywany przez właściwość Controller.

Ponadto jest to z uwagi kodu źródłowego:

TAggregatedObject i TContainedObject są odpowiednie klasy bazowe interfejsowych do obiektów, które mają być łączone lub zawarte w zewnętrznej obiektu sterującego. Używając składni "narzędzi" dla właściwości interfejsu obiektu w deklaracji zewnętrznej klasy obiektu, należy użyć tych typów do implementacji obiektu wewnętrznego.

Interfejsy implementowane przez zagregowane obiekty w imieniu kontrolera nie powinny być odróżnialne od innych interfejsów dostarczonych przez kontroler. Zagregowane obiekty nie mogą przechowywać własnej liczby referencyjnej - muszą mieć ten sam czas życia co ich kontroler. Aby to osiągnąć, zagregowane obiekty odzwierciedlają metody liczników odwołań do kontrolera.

Obiekt TAggregatedObject po prostu odzwierciedla wywołania QueryInterface do kontrolera . Z takiego zagregowanego obiektu można uzyskać dowolny interfejs obsługiwany przez kontroler i tylko interfejsy obsługiwane przez kontroler . Jest to przydatne do implementacji klasy kontrolera , która używa jednego lub więcej wewnętrznych obiektów do implementacji interfejsów zadeklarowanych w klasie kontrolera. Aggregation promuje współużytkowanie implementacji w hierarchii obiektów.

Obiekt TAggregatedObject jest dziedziną, w której większość obiektów zbiorczych powinna odziedziczyć , szczególnie jeśli jest używana w połączeniu ze składnią "toolss" .

+0

@FabricioAraujo Znalazłem dokumenty na końcu. Było literówka !! –

+0

Zostałem wprowadzony w błąd przez niezliczone przykłady, które znalazłem gdzie indziej. Ma to doskonały sens, że liczenie referencyjne musi mieć miejsce gdzieś. Dziękujemy za odpowiedź – Gryffe

+0

Nie ma za co. To było dla mnie ciekawe doświadczenie edukacyjne! –