2012-05-05 7 views

Odpowiedz

10

Zawsze używam InstanceClass.Create, jeśli jest to właściwe - i niezmiennie tak jest.

Istnieje wiele powodów. Bardzo dobry jest fakt, że wersja jednoliniowa jest bardziej zwięzła. Innym jest to, że wersja jednoliniowa jest standardowym, powszechnie stosowanym podejściem.

Kolejnym powodem jest obsługa wyjątków w konstruktorze, których metoda 1 nie działa poprawnie. W przypadku wyjątku nowa instancja zostanie zniszczona, ale zmienna instancji została jeszcze przypisana. To ważna różnica w stosunku do metody 2 i jest sprzeczna z wszystkimi konwencjami zarządzania na całe życie Delphi.

Wspominasz o TApplication.CreateForm. Rzućmy okiem na to:

Instance := TComponent(InstanceClass.NewInstance); 
TComponent(Reference) := Instance; 
try 
    Instance.Create(Self); 
except 
    TComponent(Reference) := nil; 
    raise; 
end; 

Pamiętaj, że Reference jest zmienna forma, które przechodzą jako parametr var. Chodzi o to, że ten kod przypisuje tę zmienną formularza przed wywołaniem konstruktora. Zwykle to zadanie jest wykonywane tylko po zakończeniu konstruktora.

Prawdopodobnie jest tak, że kod, który odwołuje się do zmiennej formularza (często jest to zmienna globalna), może działać, nawet jeśli jest wywołany z wewnątrz konstruktora tego formularza. Jest to bardzo szczególny przypadek i jest w przeważającej mierze wyjątkiem, a nie regułą. Nie pozwól, aby ten specjalny przypadek napędzał twój główny styl kodowania.

+0

Warto wiedzieć, dlaczego zmienna formularza jest wymagana przed zakończeniem konstruktora formularzy? – kludg

+1

@Serg Myślę, że to nieprawda. To prawdopodobnie relikt z bardzo wczesnego VCL. Wydaje się błędne użycie odniesienia do częściowo skonstruowanego obiektu. Nie uważałbym tego za dobry styl. Myślę, że to tylko dziedzictwo. –

+0

Zdecydowanie nie jest to dobry styl, ale formy mają szczególną rolę w VCL i IDE, więc może to być poważny powód. – kludg

2

Druga jest lepsza, ponieważ jest to standardowa metoda tworzenia instancji klasy, podczas gdy formularz proceduralny powinien być użyty w konstruktorze w celu wywołania konstruktora dziedziczonego.

+0

ale wbudowana metoda delphi (Application.CreateForm) używa InstanceClass.NewInstance – MajidTaheri

+0

Zgadzam się z Davidem, z jakiegoś powodu konieczne jest odwołanie do instancji formularza, zanim konstruktor formularza się skończy, chociaż nie mogę powiedzieć, z jakiego powodu . – kludg

4

(dodałem tę odpowiedź, bo IMHO gdzie inni nie kompletna)

Metoda 2 jest prawidłowa.

Metoda 1, jeśli nie zostanie wywołana, ponieważ do wywołania konstruktora jest ukryty parametr, który może nie zadziałać prawidłowo, iniatialize: w rzeczywistości NewInstance jest pseudo-wirtualną metodą klasy!

Rzeczywiście, w wywołaniu konstruktora istnieje ukryty parametr boolean (rejestr EDX, od EAX = klasa). Jak stwierdził official documentation:

konstruktorów i destruktorów używać tych samych konwencji telefoniczne innych metod, oprócz tego, że dodatkowy parametr Boolean flaga jest przekazywana do wskazania kontekstu konstruktora lub destruktora rozmowy.

Wartość False w parametrze flag wywołania konstruktora wskazuje, że konstruktor został wywołany przez obiekt instancji lub za pomocą odziedziczonego słowa kluczowego. W takim przypadku konstruktor zachowuje się tak jak zwykła metoda. Wartość True w parametrze flag wywołania konstruktora wskazuje, że konstruktor został wywołany przez odwołanie klasy . W takim przypadku konstruktor tworzy instancję z class podaną przez Self i zwraca odniesienie do nowo utworzonego obiektu w EAX.

W szczególności, po wywołaniu w ten sposób, klasa nie wywoła funkcji _ClassCreate. Inicjowanie klasy może się nie powieść, jeśli klasa nie ma być utworzona z domyślną funkcją NewInstance. W rzeczywistości ta funkcja jest wstawiana do klasy VMT: w rzadkich przypadkach może być przeciążona (np. W celu zapewnienia innego schematu alokacji pamięci - może to być zbieracz pamięci lub zoptymalizowany pod kątem szybkości program przydzielania). Dlatego wywołanie bezpośrednio InstanceClass.NewInstance może być błędne, w niektórych przypadkach granicznych.

function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject; 
asm 
    ... 
    TEST DL,DL 
    JL  @@noAlloc 
    CALL dword ptr [EAX].vmtNewInstance 
@@noAlloc: 
    ... 

Dlatego dzwoniąc InstanceClass.NewInstance bezpośrednio jest tylko zostać zrobione celowo, tylko jeśli chcesz anulować dwa nadpisane vmtNewInstance/vmtFreeInstance (aw tym przypadku może być również NIE zadzwonić .Free/.Destroy, ale jesteś właścicielem pamięci darmową funkcję). Tak więc: nigdy nie dzwoń pod numer NewInstance, ale constructor, zaprojektowany i udokumentowany przez zespół Embarcadero (i przy okazji zespół FreePascal), chyba że musisz wprowadzić jakieś wewnętrzne ulepszenia niskiego poziomu.

NIGDY nie używaj metody 1 do tworzenia obiektu! Może działać przez 99,9% czasu, ale może się nie udać w niektórych przypadkach lub z rozszerzeniem kompilatora/RTL w przyszłości (jak odśmiecacz). Nawet jeśli VCL czasami używa NewInstance, nie powinieneś go używać - wolałbym, aby ta metoda była chroniona.