2012-02-08 14 views
6

Wiem, że tytuł jest bardzo szeroki - obejmuje wiele!Generics, Polimorfizm, interfejsy: jakie jest rozwiązanie?

Mam nadzieję, że to pytanie może ewoluować do większej "informacji na wiki" na tematy.

Czego nauczyłem - do tej pory:

  • Przy korzystaniu Generics - zrozumieć koncepcje (covariance and contravariance).
  • Do NOT "mis-use" Pojęcie generyków w połączeniu z dziedziczeniem. Zrobiłem to i może to doprowadzić cię bezpośrednio do problemów związanych z kowariancją! Upewnij się, że "zerwałeś" rodzajowy we właściwym punkcie twojego dziedziczenia - jeśli łączysz te dwa.

(proszę mnie poprawić, - jeżeli uważasz, że się mylę, czy nie brakuje niczego źle).

Moim problemem było:

Ale teraz mam spędzić wiele godzin, próbując dowiedzieć się, jak rozwiązać ten „wielki puzzle” mam na moim biurku. Otrzymałem już kilka dobrych odpowiedzi od kilku z Was, ale teraz nadszedł czas, aby coś działało na większą skalę.

ośmieliłem się rodzajowych z tym jednym: Generics and Polymorphism working together

a teraz jestem trochę zakleszczony na ten jeden: Situations where Generics won't work

Dlaczego ja skończyć z problemami kowariancji - to z powodu mojej klasy w procedurze moja hierarchia.

Zastanawiam się, czy interfejsy to mój kolejny odważny ruch w tej "sadze". Jak można "przekroczyć" problem z kowariancją. Jedną z rzeczy jest dowiedzieć się, że faktycznie masz ten problem - inną rzeczą jest "jak obejść to".

Jeśli więc ktokolwiek z was, dobrych ludzi "tam", ma jakiekolwiek zdanie na ten temat - jestem uszy. Zasadniczo: Poinformuj mnie, żebym poszedł na Interfejsy (ja sam nigdy nie robiłem od zera). Albo .. rzuć mi kość w kierunku, który sugerujesz.

Moja obecna pula źródłowa jest taka, jak podano w drugim linku - od góry.

Oto mały fragment z mojego wcześniejszego post, który pokazuje mój problem z kowariancją. David uprzejmie wyjaśnione - Dlaczego wpadłem w krzak .. Ale teraz potrzebuję informacji na - Jak biegać.

var  
    aList : TBaseList<TBaseObject>; // used as a list parameter for methods 
    aPersonList : TPersonList<TPerson>; 
    aCustomerList : TCustomerList<TCustomer>; 
begin 
    aPersonList := TPersonList<TPerson>.Create; 
    aCustomerList := TCustomerList<TCustomer>.Create; 

    aList := aCustomerList; <-- this FAILS !! types not equal .. 

end; 

Pozdrowienia

+1

Pomogłoby to, gdybyśmy wiedzieli, jaki jest twój rzeczywisty problem. Z mojego doświadczenia wynika, że ​​kowariancja rzadko stanowi problem, jeśli projekt nie jest zbyt złożony. IOW, powiedz nam prawdziwy problem, więc mamy coś konkretnego do rozwiązania. –

+0

@RudyVelthuis Cóż ... jak wspomniałeś - jest to bardzo złożone i wymagałoby wiele przeróbek z byłego [post] (http://stackoverflow.com/q/9140485/696574). Ale w zasadzie jest to ten sam problem - zmieniło się tylko pytanie. Ponieważ teraz wiem, co zrobiłem źle - mam tylko nadzieję, że ktoś może wskazać mi kierunek rozwiązania. Jeśli nadal jest niejasna - daj mi znać, a ja postaram się wyjaśnić tak dobrze jak potrafię. –

+0

Sposób obejścia problemu zależy od problemu * *. Po przypisaniu wartości do 'aList', co oczekiwałeś * zrobienia * tej zmiennej? (Ponadto, dlaczego TPersonList i TCustomerList są generyczne? Jakiego * innego * możesz mieć listę TPersonList?) –

Odpowiedz

5

Nie możesz robić tego, co chcesz robić, ale tak nie jest, w każdym razie, używasz generycznych. Jak powiedział Rob Kennedy, nie ma sensu posiadanie TCustomerList<TCustomer> i TPersonList<TPerson>. Piękno generyków polega na tym, że możesz użyć tej samej listy dla różnych typów elementów. Oznacza to, że lista i typ elementu nie mogą mieć żadnych zależności.

można zrobić coś takiego:

procedure TSomething.ProcessList<T: TBaseObject>(const aList: TBaseList<T>); 
begin 
    // process the list using code that is independent of the actual type of T. 
end; 

... 

var 
    aCustomerList: TBaseList<TCustomer>; 
    aPersonList: TBaseList<TPerson>; 
begin 
    ProcessList(aCustomerList); 
    ProcessList(aPersonList); 

Być może trzeba będzie określić T (niektóre wczesne wersje generyczne nie obsługują typu wnioskowanie - tzn że inferes typ T od rodzaju parametr - bardzo dobrze), czyli

ProcessList<TCustomer>(aCustomerList); 
    ProcessList<TPerson>(aPersonList); 

Ale to, lub coś podobnego, jest tym, co powinieneś zrobić. Coś jeszcze nie ma sensu, IMO. Nie ma potrzeby posiadania zmiennej, która mogłaby pomieścić którąkolwiek z tych list, tak jak Twoja aList. A jeśli naprawdę potrzebujesz, możesz użyć tylko TObject, ale to nie pozwala ci korzystać z listy w jakikolwiek użyteczny sposób. I nie jest bardzo ogólny.

Interfejsy w ogóle nie pomogą w rozwiązaniu tego problemu. Możesz nadać klasom pewne możliwości, tj. Także elementy list, poprzez interfejsy (inny rodzaj polimorfizmu). Ale to nie poradzi sobie z kowariancją.

+0

Dozwolenie typu +1 oznacza, że ​​powinieneś być w stanie napisać to jako 'ProcessList (aCustomerList)' i 'ProcessList (aPersonList)'. –

+0

@David: Wiem, ale typ wnioskowania nie zawsze działa zgodnie z oczekiwaniami, szczególnie nie we wcześniejszych wersjach, które obsługuje generics. Zauważ, że napisałem ProcessList (itp.), Ale podjąłem zastrzeżenie, że może nie zostać zaakceptowane w ten sposób. –

+0

"Być może trzeba będzie określić" jest nieco niejasne. Myślę, że to mnie zdezorientowało. Być może lepiej wyjść i powiedzieć, że zwięzła składnia powinna działać, ale niektóre wersje kompilatora go nie lubią.Co ciekawe, ostatnio QC'd generuje błąd generyczny, który był na odwrót. Zastosowana składnia wywodu, alternatywa spowodowała błąd wewnętrzny. –

0

pójdę do:

TCustomCustomerList = class(TBaseList<TBaseObject>) 
end; 

TCustomerList = class(TCustomCustomerList) 
end; 

, czy nie jest to dopuszczalne w projekcie jest całkowicie inna sprawa. Jeśli celem, który chcesz osiągnąć, jest przypisanie TCustomerList do zmiennej TBaseList, to byłaby droga do zrobienia.

+0

Proponujesz, żebym przerwał łańcuch generyczny tuż nad definicją podstawy ... hmmm ... Będę miał pęknięcie w projekcie testowym i wrócę do ciebie. Może potrwać kilka godzin, zanim będę mógł wrócić do ciebie. –

+0

Jak widzisz, obecnie testuję ... i jak na razie wygląda na to, że pójdę z radą Rudy (w zasadzie to samo, co twoje - ale wyjaśnione bardziej szczegółowo). –

Powiązane problemy