Dlaczego należy unikać rzucania? [Zamknięte]

97

Generalnie unikam rzutowania typów tak bardzo, jak to możliwe, ponieważ mam wrażenie, że jest to zła praktyka kodowania i może spowodować spadek wydajności.

Ale gdyby ktoś poprosił mnie o wyjaśnienie, dlaczego dokładnie tak jest, prawdopodobnie patrzyłbym na nich jak jeleń w reflektorach.

Więc dlaczego / kiedy casting jest zły?

Czy jest to ogólne rozwiązanie dla języków Java, C #, C ++, czy też każde inne środowisko wykonawcze radzi sobie z nim na własnych warunkach?

Mile widziane są specyfikacje dla dowolnego języka, na przykład dlaczego jest zły w języku c ++?

Głośno NIE Prawdopodobnie źle
źródło
3
Skąd masz takie wrażenie?
Oded
8
Mam takie wrażenie, ponieważ nigdy nie czytałem książki ani nie spotkałem programisty, który powiedziałby „CASTING SOOOO GOOOD !!!”
Głośno N Prawdopodobnie źle
15
Odpowiedź dla C ++ jest nieuchronnie inna niż odpowiedź dla C #. Jest to bardzo specyficzne dla języka. Dotychczasowe odpowiedzi odpowiadają na to pytanie dla określonych języków, aw niektórych przypadkach nie podają, o jakim języku mówią.
James McNellis,
2
Zły to termin względny. Unikanie rzutowania jest najlepszą praktyką, ale czasami programista musi zrobić to, co musi zrobić. (zwłaszcza jeśli piszesz program w Javie 1.5+, który używa biblioteki napisanej dla 1.4) Być może zmień nazwę pytania „Dlaczego należy unikać rzutowania?”
Mike Miller,
2
To świetne pytanie… Zostało zamknięte przez 5 użytkowników, z których każdy ma mniej niż 10 000 reputacji !! Najwyraźniej nadgorliwe użycie nowych mocy. Głosowałem za ponownym otwarciem.
reach4thelasers

Odpowiedzi:

141

Oznaczyłeś to trzema językami, a odpowiedzi są bardzo różne w tych trzech. Dyskusja o C ++ mniej lub bardziej implikuje również dyskusję o rzutach C, co daje (mniej więcej) czwartą odpowiedź.

Ponieważ jest to ten, o którym nie wspomniałeś wprost, zacznę od C. Rzuty C mają wiele problemów. Jednym z nich jest to, że mogą robić wiele różnych rzeczy. W niektórych przypadkach rzutowanie nie robi nic poza informacją kompilatora (w zasadzie): „zamknij się, wiem, co robię” - tj. Zapewnia, że ​​nawet gdy wykonujesz konwersję, która może powodować problemy, kompilator nie ostrzeże Cię o tych potencjalnych problemach. Na przykład char a=(char)123456;. Dokładny wynik tej implementacji zdefiniowany (zależy od rozmiaru i podpisuchar) iz wyjątkiem dość dziwnych sytuacji, prawdopodobnie nie jest przydatny. Rzuty C różnią się również tym, czy są czymś, co dzieje się tylko w czasie kompilacji (tj. Po prostu mówisz kompilatorowi, jak interpretować / traktować niektóre dane), czy czymś, co dzieje się w czasie wykonywania (np. Rzeczywista konwersja z double do długie).

C ++ próbuje sobie z tym poradzić, przynajmniej do pewnego stopnia, dodając kilka „nowych” operatorów rzutowania, z których każdy jest ograniczony tylko do podzbioru możliwości rzutowania w C. Utrudnia to (na przykład) przypadkowe wykonanie konwersji, której tak naprawdę nie zamierzałeś - jeśli zamierzasz tylko odrzucić stałość na obiekcie, możesz użyć const_casti mieć pewność, że jedyną rzeczą, na którą może wpłynąć, jest to, czy celem jest const, volatilealbo nie. I odwrotnie, a static_castnie może wpływać na to, czy obiekt jest constczyvolatile. Krótko mówiąc, masz większość tych samych typów możliwości, ale są one podzielone na kategorie, więc jedno rzutowanie może generalnie wykonać tylko jeden rodzaj konwersji, gdzie pojedyncze rzutowanie w stylu C może wykonać dwie lub trzy konwersje w jednej operacji. Podstawowym wyjątkiem jest to, że możesz użyć dynamic_castzamiast a static_castprzynajmniej w niektórych przypadkach i mimo że zostanie zapisane jako a dynamic_cast, tak naprawdę skończy się jako static_cast. Na przykład, możesz użyć dynamic_castdo przechodzenia w górę lub w dół hierarchii klas - ale rzutowanie „w górę” hierarchii jest zawsze bezpieczne, więc można to zrobić statycznie, podczas gdy rzutowanie „w dół” hierarchii niekoniecznie jest bezpieczne, więc wykonywane dynamicznie.

Java i C # są do siebie znacznie bardziej podobne. W szczególności, w przypadku obu z nich rzutowanie jest (praktycznie?) Zawsze operacją wykonywaną. Jeśli chodzi o operatory rzutowania C ++, jest zwykle najbliżej a dynamic_castpod względem tego, co naprawdę zostało zrobione - tj. Kiedy próbujesz rzutować obiekt na jakiś typ docelowy, kompilator wstawia sprawdzenie w czasie wykonywania, aby zobaczyć, czy ta konwersja jest dozwolona i zgłoś wyjątek, jeśli tak nie jest. Dokładne szczegóły (np. Nazwa użyta dla wyjątku „złe rzucanie”) są różne, ale podstawowa zasada pozostaje w większości podobna (chociaż, jeśli pamięć służy, Java sprawia, że ​​rzutowania są stosowane do kilku typów niebędących obiektami, takich jak intznacznie bliższe C rzuty - ale te typy są używane na tyle rzadko, że 1) nie pamiętam tego na pewno i 2) nawet jeśli to prawda, i tak nie ma to większego znaczenia).

Patrząc na rzeczy bardziej ogólnie, sytuacja jest dość prosta (przynajmniej IMO): rzutowanie (oczywiście) oznacza, że ​​konwertujesz coś z jednego typu na inny. Kiedy / jeśli to zrobisz, pojawi się pytanie „Dlaczego?” Jeśli naprawdę chcesz, aby coś było określonym typem, dlaczego nie zdefiniowałeś tego jako tego typu? Nie oznacza to, że nigdy nie ma powodu, aby wykonywać taką konwersję, ale za każdym razem, gdy to nastąpi, powinno nasunąć pytanie, czy można przeprojektować kod, tak aby cały czas był używany prawidłowy typ. Nawet pozornie nieszkodliwe konwersje (np. Między liczbami całkowitymi a zmiennoprzecinkowymi) powinny być zbadane znacznie dokładniej niż jest to powszechne. Pomimo ich pozorupodobieństwo, liczby całkowite powinny być naprawdę używane dla „policzonych” typów rzeczy, a zmiennoprzecinkowe dla „mierzonych” rodzajów rzeczy. Ignorowanie tego rozróżnienia prowadzi do niektórych szalonych stwierdzeń, takich jak „przeciętna amerykańska rodzina ma 1,8 dzieci”. Chociaż wszyscy możemy zobaczyć, jak to się dzieje, faktem jest, że żadna rodzina nie ma 1,8 dzieci. Mogą mieć 1, 2 lub więcej, ale nigdy 1,8.

Jerry Coffin
źródło
10
Wygląda na to, że cierpisz tutaj na „śmierć przez czas skupienia”, to jest miła odpowiedź.
Steve Townsend
2
Mocna odpowiedź, ale kilka części „Nie wiem” można by zawęzić, aby była wyczerpująca. @ Dragontamer5788 to dobra odpowiedź, ale nie wyczerpująca.
M2tM
5
Jedno całe dziecko i jedno dziecko z brakującą nogą to 1,8 dziecka. Ale mimo wszystko bardzo miła odpowiedź. :)
Oz.
1
Bardzo fajna odpowiedź, nieskrępowana w najmniejszym stopniu wykluczeniem par bez dzieci.
@Roger: nie wyklucza, tylko ignoruje.
Jerry Coffin
48

Wiele dobrych odpowiedzi. Oto jak na to patrzę (z perspektywy C #).

Przesyłanie zwykle oznacza jedną z dwóch rzeczy:

  • Znam typ środowiska wykonawczego tego wyrażenia, ale kompilator go nie zna. Kompilator, mówię ci, w czasie wykonywania obiekt, który odpowiada temu wyrażeniu, naprawdę będzie tego typu. Na razie wiesz, że to wyrażenie należy traktować jako tego typu. Wygeneruj kod, który zakłada, że ​​obiekt będzie danego typu lub wyrzuć wyjątek, jeśli się mylę.

  • Zarówno kompilator, jak i programista znają typ środowiska wykonawczego wyrażenia. Istnieje inna wartość innego typu skojarzona z wartością, którą to wyrażenie będzie miało w czasie wykonywania. Generuj kod, który tworzy wartość żądanego typu z wartości danego typu; jeśli nie możesz tego zrobić, zgłoś wyjątek.

Zauważ, że to są przeciwieństwa . Istnieją dwa rodzaje odlewów! Istnieją rzuty, w których dajesz kompilatorowi wskazówkę dotyczącą rzeczywistości - hej, ten obiekt typu jest w rzeczywistości typu Customer - i są rzuty, w których mówisz kompilatorowi, aby wykonał mapowanie z jednego typu na inny - hej, Potrzebuję int, który odpowiada temu podwójnemu.

Oba rodzaje rzutów to czerwone flagi. Pierwszy rodzaj rzutowania rodzi pytanie "dlaczego dokładnie programista wie coś, czego nie ma kompilator?" Jeśli jesteś w takiej sytuacji, to lepiej zrobić to zwykle zmienić program tak, że kompilator nie posiada uchwyt na rzeczywistość. Wtedy nie potrzebujesz obsady; analiza jest wykonywana w czasie kompilacji.

Drugi rodzaj rzutowania rodzi pytanie „dlaczego operacja nie jest wykonywana w pierwszej kolejności w docelowym typie danych?” Jeśli potrzebujesz wyniku w int, to dlaczego w pierwszej kolejności trzymasz dubla? Nie powinieneś trzymać int?

Kilka dodatkowych myśli:

http://blogs.msdn.com/b/ericlippert/archive/tags/cast+operator/

Eric Lippert
źródło
2
Nie sądzę, że są to z natury czerwone flagi. Jeśli masz Fooobiekt, który dziedziczy po nim Bari przechowujesz go w a List<Bar>, będziesz potrzebować rzutów, jeśli chcesz to z Foopowrotem. Być może wskazuje to na problem na poziomie architektonicznym (dlaczego przechowujemy Bars zamiast Foos?), Ale niekoniecznie. A jeśli Fooma to również prawidłową obsadę int, dotyczy to również Twojego innego komentarza: Przechowujesz a Foo, a nie an int, ponieważ intnie zawsze jest odpowiednie.
Mike Caron
6
@Mike Caron - nie mogę oczywiście odpowiedzieć za Erica, ale dla mnie czerwona flaga oznacza „to jest coś do przemyślenia”, a nie „to jest coś złego”. I nie ma problemu z przechowywaniem a Foow a List<Bar>, ale w miejscu obsady próbujesz zrobić coś, Fooco nie jest odpowiednie dla Bar. Oznacza to, że różne zachowania dla podtypów są wykonywane przez mechanizm inny niż wbudowany polimorfizm zapewniany przez metody wirtualne. Może to dobre rozwiązanie, ale częściej jest to czerwona flaga.
Jeffrey L Whitledge,
14
W rzeczy samej. Jeśli wyciągasz rzeczy z listy zwierząt, a później musisz powiedzieć kompilatorowi, och, tak przy okazji, wiem, że pierwszy to tygrys, drugi to lew, a trzeci jest niedźwiedziem, więc powinieneś używać Tuple <Lion, Tiger, Bear>, a nie List <Animal>.
Eric Lippert
5
Zgadzam się z tobą, ale myślę, że bez lepszej obsługi składniowej Tuple<X,Y,...>krotek jest mało prawdopodobne, aby zobaczył powszechne zastosowanie w C #. Jest to jedyne miejsce, w którym język mógłby skuteczniej popychać ludzi do „dołu sukcesu”.
kvb
4
@kvb: Zgadzam się. Rozważaliśmy przyjęcie składni krotki dla języka C # 4, ale nie mieściła się ona w budżecie. Być może w C # 5; nie opracowaliśmy jeszcze pełnego zestawu funkcji. Zbyt zajęty łączeniem CTP do asynchronizacji. A może w hipotetycznej przyszłej wersji.
Eric Lippert
36

Błędy przesyłania są zawsze zgłaszane jako błędy czasu wykonywania w języku Java. Używanie typów ogólnych lub szablonów zmienia te błędy w błędy w czasie kompilacji, co znacznie ułatwia wykrycie błędu.

Jak powiedziałem powyżej. Nie oznacza to, że wszystkie castingi są złe. Ale jeśli można tego uniknąć, najlepiej to zrobić.

Mike Miller
źródło
4
Zakłada się, że wszystkie castingi są „złe”; jednak nie jest to do końca prawdą. Weźmy na przykład C # z niejawną i jawną obsługą rzutowania. Problem pojawia się, gdy wykonywane jest rzutowanie, które (przypadkowo) usuwa informacje lub bezpieczeństwo typu (różni się to w zależności od języka).
5
Właściwie to jest całkowicie błędne w C ++. Być może konieczna jest zmiana uwzględniająca języki, na które są kierowane te informacje.
Steve Townsend
@Erick - chciałbym, ale nie znam pierwszej rzeczy o Javie ani wystarczająco dużo szczegółów C #, aby mieć pewność, że informacje są poprawne.
Steve Townsend
Przepraszam, jestem facetem od Javy, więc moja znajomość C ++ jest wystarczająco ograniczona. Mógłbym usunąć z niego słowo „szablonowanie”, ponieważ miało być niejasne.
Mike Miller,
Skąd. Kompilator Java przechwytuje niektóre błędy rzutowania, a nie tylko błędy ogólne. Rozważ (String) 0;
Markiz Lorne
18

Casting nie jest z natury zły, po prostu często jest nadużywany jako środek do osiągnięcia czegoś, czego naprawdę nie powinno się robić w ogóle lub robić bardziej elegancko.

Gdyby był ogólnie zły, języki by go nie wspierały. Jak każda inna funkcja językowa ma swoje miejsce.

Moją radą byłoby skupienie się na swoim podstawowym języku i zrozumienie wszystkich jego obsady oraz powiązanych najlepszych praktyk. To powinno informować wycieczki w innych językach.

Odpowiednie dokumenty C # są tutaj .

W poprzednim pytaniu SO znajduje się świetne podsumowanie opcji C ++ .

Steve Townsend
źródło
9

Mówię tutaj głównie o C ++ , ale większość z tego prawdopodobnie dotyczy również Java i C #:

C ++ jest językiem z typowaniem statycznym . Istnieją pewne możliwości, na jakie pozwala język (funkcje wirtualne, niejawne konwersje), ale w zasadzie kompilator zna typ każdego obiektu w czasie kompilacji. Powodem używania takiego języka jest to, że błędy można wykryć w czasie kompilacji . Jeśli kompilator zna typy ai b, złapie Cię w czasie kompilacji, gdy zrobisz a=bgdzie ajest liczbą zespoloną i bciągiem.

Ilekroć wykonujesz rzutowanie jawne, mówisz kompilatorowi, aby się zamknął, ponieważ myślisz, że wiesz lepiej . Jeśli się mylisz, zwykle dowiesz się o tym dopiero w czasie wykonywania . Problem ze stwierdzeniem w czasie wykonywania polega na tym, że może to być u klienta.

sbi
źródło
5

Java, c # i c ++ są językami silnie wpisanymi typami, chociaż języki silnie typowane mogą być postrzegane jako nieelastyczne, mają tę zaletę, że sprawdzają typ w czasie kompilacji i chronią przed błędami w czasie wykonywania, które są spowodowane niewłaściwym typem dla niektórych operacji.

Istnieją zasadniczo dwa rodzaje rzutów: rzut do bardziej ogólnego typu lub rzut do innego typu (bardziej szczegółowy). Rzutowanie na typ bardziej ogólny (rzutowanie na typ nadrzędny) pozostawi kontrolę czasu kompilacji nienaruszoną. Jednak rzutowanie na inne typy (bardziej szczegółowe typy) wyłączy sprawdzanie typów w czasie kompilacji i zostanie zastąpione przez kompilator sprawdzeniem w czasie wykonywania. Oznacza to, że masz mniejszą pewność, że skompilowany kod będzie działał poprawnie. Ma również niewielki wpływ na wydajność, ze względu na dodatkowe sprawdzanie typu w czasie wykonywania (API Java jest pełne rzutów!).

Kdeveloper
źródło
5
Nie wszystkie rzucają zabezpieczenia typu „bypass” w C ++.
James McNellis
4
Nie wszystkie rzutowania bezpieczeństwa typu „obejście” w języku C #.
5
Nie wszystkie rzutowania bezpieczeństwa typu „obejście” w Javie.
Erick Robertson
11
Nie wszystkie odlewy typu „bypass” są bezpieczne w Casting. Och, czekaj, ten tag nie odnosi się do języka ...
sbi
2
W prawie wszystkich przypadkach w C # i Javie rzutowanie spowoduje pogorszenie wydajności, ponieważ system przeprowadzi sprawdzenie typu w czasie wykonywania (co nie jest bezpłatne). W C ++ dynamic_castjest generalnie wolniejszy niż static_cast, ponieważ generalnie musi sprawdzać typ w czasie wykonywania (z pewnymi zastrzeżeniami: rzutowanie na typ podstawowy jest tanie itp.).
Travis Gockel
4

Niektóre rodzaje rzutów są tak bezpieczne i wydajne, że często w ogóle nie można ich uznać za rzucanie.

Jeśli rzutujesz z typu pochodnego na typ podstawowy, jest to generalnie dość tanie (często - w zależności od języka, implementacji i innych czynników - jest zerowe) i bezpieczne.

Jeśli rzucasz z prostego typu, takiego jak int, do szerszego typu, takiego jak long int, to znowu jest to dość tanie (generalnie niewiele droższe niż przypisanie tego samego typu, do którego jest rzutowany) i znowu jest bezpieczne.

Inne typy są bardziej najeżone i / lub droższe. W większości języków rzutowanie z typu podstawowego do typu pochodnego jest albo tanie, ale wiąże się z wysokim ryzykiem poważnego błędu (w C ++, jeśli static_cast z bazowego na pochodny, będzie to tanie, ale jeśli wartość bazowa nie jest typu pochodnego, zachowanie jest niezdefiniowane i może być bardzo dziwne) lub stosunkowo drogie i wiąże się z ryzykiem wywołania wyjątku (dynamic_cast w C ++, jawne rzutowanie z bazy na pochodne w C # i tak dalej). Boksowanie w Javie i C # to kolejny tego przykład i jeszcze większy koszt (biorąc pod uwagę, że zmieniają się one bardziej niż tylko sposób traktowania bazowych wartości).

Inne typy rzutowania mogą spowodować utratę informacji (długi typ całkowity na krótki typ całkowity).

Te przypadki ryzyka (wyjątek lub poważniejszy błąd) i kosztów są powodem, dla którego należy unikać rzucania.

Bardziej konceptualnym, ale być może ważniejszym powodem jest to, że każdy przypadek rzutowania jest przypadkiem, w którym twoja zdolność rozumowania poprawności kodu jest utrudniona: każdy przypadek to inne miejsce, w którym coś może pójść nie tak i sposoby, w jakie może się nie udać, co dodatkowo komplikuje wnioskowanie, czy system jako całość się nie powiedzie. Nawet jeśli obsada jest za każdym razem pewnie bezpieczna, udowodnienie tego jest dodatkową częścią rozumowania.

Wreszcie, intensywne użycie rzutów może wskazywać na niepowodzenie we właściwym rozważeniu modelu obiektowego podczas jego tworzenia, używania lub obu tych rzeczy: Rzutowanie w przód iw tył między tymi samymi kilkoma typami często prawie zawsze kończy się niepowodzeniem w rozważeniu relacji między typami używany. Tutaj nie chodzi o to, że odlewy są złe, ale są oznaką czegoś złego.

Jon Hanna
źródło
2

Programiści mają coraz większą tendencję do trzymania się dogmatycznych zasad dotyczących używania funkcji językowych („nigdy nie używaj XXX!”, „XXX uważane za szkodliwe” itp.), Gdzie XXX waha się od gotos do wskaźników do protectedczłonków danych, pojedynczych do przechodzących obiektów wartość.

Z mojego doświadczenia wynika, że ​​przestrzeganie takich zasad zapewnia dwie rzeczy: nie będziesz ani złym programistą, ani świetnym programistą.

O wiele lepszym rozwiązaniem jest wykopać dół i odkryć ziarno prawdy za tymi zakazami koc, a następnie użyj funkcji rozważnie, ze zrozumieniem, że tam w wielu sytuacjach, dla których są one najlepszym narzędziem do pracy.

„Generalnie unikam rzucania typów tak bardzo, jak to możliwe” jest dobrym przykładem takiej nadmiernie uogólnionej reguły. Gipsy są niezbędne w wielu typowych sytuacjach. Kilka przykładów:

  1. Podczas współdziałania z kodem stron trzecich (zwłaszcza gdy ten kod jest pełen typedef). (Przykład: GLfloat<--> double<-->Real .)
  2. Rzutowanie ze wskaźnika / referencji klasy pochodnej do klasy bazowej: jest to tak powszechne i naturalne, że kompilator zrobi to niejawnie. Jeśli uczynienie tego jawnym zwiększa czytelność, rzutowanie jest krokiem do przodu, a nie do tyłu!
  3. Rzutowanie z bazy do wskaźnika / odwołania klasy pochodnej: również powszechne, nawet w dobrze zaprojektowanym kodzie. (Przykład: heterogeniczne pojemniki.)
  4. Wewnątrz serializacji / deserializacji binarnej lub innego kodu niskiego poziomu, który wymaga dostępu do nieprzetworzonych bajtów typów wbudowanych.
  5. Zawsze, gdy użycie innego typu jest po prostu bardziej naturalne, wygodne i czytelne. (Przykład: std::size_type-> int.)

Z pewnością jest wiele sytuacji, w których użycie obsady nie jest właściwe i ważne jest, aby się ich nauczyć; Nie będę wchodził w zbyt wiele szczegółów, ponieważ powyższe odpowiedzi wykonały dobrą robotę, wskazując niektóre z nich.

user168715
źródło
1

Aby rozwinąć odpowiedź KDevelopera , nie jest ona z natury bezpieczna dla typów. W przypadku przesyłania nie ma gwarancji, że to, z czego przesyłasz i do czego, będzie pasować, a jeśli tak się stanie, otrzymasz wyjątek czasu wykonywania, co zawsze jest złe.

W odniesieniu do języka C #, ponieważ zawiera on operatory isi as, masz możliwość (w większości) określenia, czy rzutowanie powiedzie się. Z tego powodu należy podjąć odpowiednie kroki, aby określić, czy operacja zakończy się powodzeniem, i postępować prawidłowo.

casperOne
źródło
1

W przypadku języka C # należy być bardziej ostrożnym podczas rzutowania ze względu na narzuty związane z pakowaniem / rozpakowywaniem podczas zajmowania się typami wartości.

Manish Basantani
źródło
1

Nie jestem pewien, czy ktoś już o tym wspomniał, ale w C # rzutowanie może być używane w dość bezpieczny sposób i często jest konieczne. Załóżmy, że otrzymujesz obiekt, który może być kilku typów. Używając issłowa kluczowego, możesz najpierw potwierdzić, że obiekt jest rzeczywiście tego typu, na który chcesz go rzutować, a następnie bezpośrednio rzutować obiekt na ten typ. (Nie pracowałem dużo z Javą, ale jestem pewien, że jest na to również bardzo prosty sposób).

user472875
źródło
1

Obiekt jest rzutowany na pewien typ tylko wtedy, gdy spełnione są 2 warunki:

  1. wiesz, że to jest tego typu
  2. kompilator tego nie robi

Oznacza to, że nie wszystkie posiadane informacje są dobrze reprezentowane w używanej strukturze typów. To jest złe, ponieważ twoja implementacja powinna być semantyczna zawierać twój model, czego wyraźnie nie ma w tym przypadku.

Teraz, kiedy wykonujesz rzut, może to mieć 2 różne powody:

  1. Zrobiłeś złą robotę w wyrażaniu relacji między typami.
  2. system typów języków po prostu nie jest wystarczająco wyrazisty, aby je sformułować.

W większości języków często napotykasz drugą sytuację. Generics jak w Javie trochę pomagają, system szablonów C ++ jeszcze bardziej, ale jest trudny do opanowania i nawet wtedy niektóre rzeczy mogą być niemożliwe lub po prostu nie warte wysiłku.

Można więc powiedzieć, że obsada to brudny hack mający na celu obejście twoich problemów i wyrażenie określonej relacji między typami w jakimś konkretnym języku. Należy unikać brudnych hacków. Ale bez nich nie możesz żyć.

back2dos
źródło
0

Ogólnie szablony (lub typy ogólne) są bardziej bezpieczne dla typów niż rzutowania. W związku z tym powiedziałbym, że problemem związanym z odlewaniem jest bezpieczeństwo typu. Jest jednak inna, bardziej subtelna kwestia związana zwłaszcza z poniżaniem: projekt. Przynajmniej z mojej perspektywy depresja to zapach kodu, wskazanie, że coś może być nie tak z moim projektem i powinienem to zbadać dalej. Dlaczego jest proste: jeśli dobrze „zrozumiesz” abstrakcje, po prostu ich nie potrzebujesz! Przy okazji fajne pytanie ...

Twoje zdrowie!

Lefteris Laskaridis
źródło
0

Aby być naprawdę zwięzłym, dobrym powodem jest przenośność. Różne architektury, które obsługują ten sam język, mogą mieć, powiedzmy, różne rozmiary int. Jeśli więc przeprowadzę migrację z ArchA do ArchB, który ma węższe int, w najlepszym razie mogę zobaczyć dziwne zachowanie, a w najgorszym - błędy seg.

(Wyraźnie ignoruję kod bajtowy niezależny od architektury i IL.)

kmarks2
źródło