2009-11-08 8 views
9

Co to jest najlepsza praktyka dla zwracania prostych obiektów z funkcji/procedur w delphi?Uzyskiwanie obiektu w wyniku func/proc w Delphi

np. 2 rodzaje kodu:

wprost stworzony obiekt jako odniesienie, zapełnić obiekt w Proc, zniszczy go potem

procedure Proc(var Obj: TMyObject); 
begin 
    // populate Obj 
end; 

O := TMyObject.Create; 
try 
    Proc(O); 
    // manipulate populated object 
finally 
    O.Free; 
end; 

lub się utworzony obiekt jako rezultat z funkcji, zniszczyć po manipulacji

function Func: TMyObj; 
begin 
    Result := TMyObj.Create; 
end; 

O := Func; 
if O <> nil then 
begin 
    try 
    // manipulate 
    finally 
    O.Free; 
    end; 
end; 

Odpowiedz

8

Nie ma najlepszych praktyk. Podstawową rzeczą, jaką należy zrobić, jest jednak to, aby upewnić się, że zawsze jest jasne, kto jest odpowiedzialny za zniszczenie przedmiotu w danym momencie, nawet gdy wystąpi wyjątek.

Nie ma nic złego z funkcją tworzenia nowej instancji i zwrot. Taką funkcją jest factory. Można traktować go jak konstruktora klasy, więc należy upewnić się, że zachowuje się jak konstruktor: albo powrót ważny obiekt lub wyjątek. Nigdy nie zwraca referencji zerowej.

function Func: TMyObj; 
begin 
    Result := TMyObj.Create; 
    try 
    Result.X := Y; 
    except 
    Result.Free; 
    raise; 
    end; 
end; 

To schemat obsługi wyjątków, którego nie widać zbyt często, ale jest ważny dla tego stylu funkcji. Wracając własności obiektu transfery z funkcji do rozmówcy, ale tylko wtedy, gdy uda się całkowicie wykonać. Jeśli musi wyjść wcześniej z powodu wyjątku, uwalnia obiekt, ponieważ osoba dzwoniąca nie ma możliwości jego uwolnienia. (Funkcje, które kończą się z powodu wyjątku nie mają wartości zwracanych.) Dzwoniący będzie go używać tak:

O := Func; 
try 
    writeln(O.X); 
finally 
    O.Free; 
end; 

Jeśli istnieje wyjątek w Func następnie O nigdy nie zostanie przypisany, więc nie ma nic dostępny dla rozmówcy do wolny.


Kiedy rozmówca tworzy obiekt i przekazać je do innej funkcji, aby ją zainicjować, nie rób parametru parametr „var”. Wprowadza to pewne ograniczenia dla osoby dzwoniącej, która musi używać zmiennej dokładnie typu żądanego przez funkcję, nawet jeśli został utworzony jakiś typ potomka.

Taka funkcja powinna , a nie uwolnić obiektu. Osoba dzwoniąca nie nadaje odpowiedzialności za funkcje, które wywołuje, szczególnie gdy planuje użyć obiektu po powrocie funkcji.

+0

dziękuję za skontaktowanie się ze mną :) wspaniała odpowiedź –

4

To zależy od czasu życia obiektu i od tego, kto jest za niego odpowiedzialny. W większości przypadków obiekty powinny być tworzone i niszczone przez tę samą jednostkę.

Załóżmy, że twoja metoda wypełnia TStringList wynikami z parsowania pliku. Powinieneś pozwolić tej funkcji utworzyć TStringList, czy powinieneś go utworzyć i przekazać jako referencję?

Uważam, że jest bardziej czytelny, aby go utworzyć, przekazać jako odniesienie, a następnie niszczyć, wszystkie w kolejnych wierszach kodu.

Teraz załóżmy, że masz funkcję, która zwraca klienta, dla każdego dodanego klienta. W takim przypadku użyłbym funkcji, ponieważ przypuszczam, że mój podmiot miałby listę lub coś w rodzaju klientów odpowiedzialnych za niszczenie ich, gdy nie były potrzebne.

-3

często zastosowania konstruktu

FUNCTION SomeFunction(SL : TStrings = NIL) : TStrings; 
    BEGIN 
    IF Assigned(SL) THEN Result:=SL ELSE Result:=TStringList.Create; 
    // Use Result for the remainder of the function 
    END; 

W ten sposób, można go używać zarówno jako procedura z przekazywana w odniesieniu, jak i funkcji, która tworzy sam przypadek.

+2

-1 Uważam, że twoja praktyka jest bardzo myląca. Wolę stworzyć dwie metody (jedna wywołująca drugą po utworzeniu obiektu) i sprawić, że nazwa metody będzie wyraźnie wyrażać, co się dzieje. Co jeśli chcesz wywołać 'SomeFunction (Obj)', gdy 'Obj' jest parametrem własnej metody? Nie możesz się dowiedzieć, czy to 'nil' czy nie, więc nie wiesz, czy musisz zwolnić zwracany obiekt czy nie ... – jpfollenius

+0

Nie rozumiem twojego komentarza" Co jeśli chcesz zadzwonić do SomeFunction (Obj) kiedy Obj jest parametrem własnej metody?Nie możesz wiedzieć, czy jest to zero czy nie, więc nie możesz wiedzieć, czy musisz zwolnić zwracany obiekt, czy nie ... "Co masz na myśli przez" kiedy Obj jest parametrem własnej metody? "? ? Jeśli mógłbyś rozwinąć przykład kodu, to z chęcią ci to wyjaśnię ... – HeartWare

+0

Chodzi o to, że powinieneś pisać metody w taki sposób, że każdy służy jednemu celowi, a nie robi więcej operacji w jednej metodzie, dlatego twoje rozwiązanie jest mylące i nie jest proste –

2

Moją zasadą jest posiadanie własności i tworzenie całości. Zawsze mam twórcę, który jest jego właścicielem i tym samym ponosi odpowiedzialność za niszczenie obiektu. Utworzenie obiektu jest jawne w kodzie wywołania, nigdy nie jest skutkiem ubocznym wywołania.

więc zwykłe podpisy moich funkcji są

function Func(o:tMyO): TMyO; 
begin 
    // .... 
    Result := o; 
end; 

ten sposób można zrobić albo

o := func(TMyO.create); 

lub

o := TMyO.create; 
    // ... 
    func(o); 
3

Jest to wspólne Delphi idiom pozwolić rozmówcy utwórz obiekt i przekaż go jako parametr. Pamiętaj, że nie musisz zadeklarować go w prawie wszystkich przypadkach jako var.

procedure Proc (Obj : TMyObject) 
begin 
    Obj.SomeProperty := 'SomeValue'; 
    ... 
end; 

Wywołanie Kod:

Obj := TMyObject.Create; 
try 
    Proc (Obj); 
finally 
    FreeAndNil (Obj); 
end;  

to uniknąć nieporozumień o tym, kto ma uwolnić obiekt. Zauważ, że jeśli masz łańcuch wywołań metod, to może się to bardzo skomplikować, jeśli chcesz śledzić obiekty, które muszą zostać zwolnione gdzieś wzdłuż linii.

Kolejna wada: tworzenie i niszczenie rozproszone w kodzie uniemożliwia użycie bloków try...finally, co jest po prostu kolejnym pomocnym idiomem, aby uniknąć przecieków zasobów.

Jeśli chcesz, aby twoja metoda stworzyła obiekt, chciałbym, aby było wyraźne w nazwie funkcji, coś jak CreateAndInitializeList brzmi dobrze dla mnie.

1

Jak wspomniano wcześniej, ogólnie tego samego podmiotu, utworzonego obiektu należy go uwolnić co oznacza, że ​​abonent wywołujący powinien utworzenia odniesienia do obiektu zamiast to zrobić wewnątrz funkcji.

Jest to jednak możliwe tylko wtedy, gdy dzwoniący zna dokładny typ zwracanego przedmiotu, a nie nadtyp. Na przykład:

var E: TEmployee; 

E := CreateEmployee(EmployeeID); // Could return TEmployee or subclasses TManager or TRetiredEmployee 

try 

    E.SendEmail(MessageText); 
    if (E is TRetiredEmployee) then 
     E.PrintLetter; 

finally 

    E.Free; 

end; 

w takich przypadkach uważam, że jest to pomocne zawierać słowo „Utwórz” lub innego wskaźnika, nazwy funkcji fabrycznego dzwonię.