Na blogach programistycznych, przykładach kodu online, a ostatnio nawet w książce, wciąż się potykam o kod:
var y = x as T;
y.SomeMethod();
lub, co gorsza:
(x as T).SomeMethod();
To nie ma dla mnie sensu. Jeżeli jesteś pewien, że x
jest typu T
, należy użyć bezpośredniego obsady: (T)x
. Jeśli nie masz pewności, możesz użyć, as
ale musisz sprawdzić null
przed wykonaniem jakiejś operacji. Wszystko, co robi powyższy kod, polega na przekształceniu (użytecznego) InvalidCastException
w (bezużyteczne) NullReferenceException
.
Czy jestem jedynym, który uważa, że to rażące nadużycie as
słowa kluczowego? Czy może przeoczyłem coś oczywistego i powyższy wzór ma sens?
c#
casting
type-conversion
Heinzi
źródło
źródło
((T)x).SomeMethod()
, prawda? ;) (żartuję, masz rację oczywiście!)(f as T).SomeMethod()
Odpowiedzi:
Twoje zrozumienie jest prawdziwe. To brzmi jak próba mikrooptymalizacji. Powinieneś użyć normalnej obsady, gdy masz pewność co do typu. Oprócz generowania bardziej sensownego wyjątku, również szybko zawiedzie. Jeśli mylisz o swoim założeniu o rodzaju, program nie będzie natychmiast i będziesz mógł zobaczyć przyczynę awarii od razu, zamiast czekać na
NullReferenceException
lubArgumentNullException
lub nawet kiedyś logicznego błędu w przyszłości. Ogólnie rzecz biorąc,as
wyrażenie, po którym nie następujenull
sprawdzenie, to zapach kodu.Z drugiej strony, jeśli nie masz pewności co do rzutu, powinieneś użyć
as
zamiast normalnego rzutu owiniętegotry-catch
blokiem. Ponadto zaleca się użycieas
zamiast kontroli typu, a następnie rzutowania. Zamiast:który generuje
isinst
instrukcję dlais
słowa kluczowego icastclass
instrukcję dla obsady (skutecznie wykonując rzut dwukrotnie), powinieneś użyć:To tylko generuje
isinst
instrukcję. Pierwsza metoda ma potencjalną wadę w aplikacjach wielowątkowych, ponieważ warunek wyścigu może spowodować, że zmienna zmieni swój typ pois
pomyślnym sprawdzeniu i nie powiedzie się na linii rzutowania. Druga metoda nie jest podatna na ten błąd.Poniższe rozwiązanie nie jest zalecane do użycia w kodzie produkcyjnym. Jeśli naprawdę nie znosisz tak fundamentalnej konstrukcji w języku C #, możesz rozważyć przejście na VB lub inny język.
Jeśli ktoś desperacko nienawidzi składni obsady, może napisać metodę rozszerzenia naśladującą obsadę:
i użyj ładnej składni [?]:
źródło
cache
obiekt, który inny wątek próbuje unieważnić, ustawiając go nanull
. W scenariuszach bez blokady takie rzeczy mogą się pojawić.Object
. Zastosowanie metody dla typu wartości spowoduje, że będzie niepotrzebnie zapakowane w ramkę.To
, ponieważ konwertuje on tylko w hierarchii dziedziczenia, która dla typów wartości i tak wymaga boksu. Oczywiście cały pomysł jest bardziej teoretyczny niż poważny.IMHO,
as
po prostu sens w połączeniu znull
czekiem:źródło
Użycie „as” nie stosuje konwersji zdefiniowanych przez użytkownika, a obsada użyje ich w stosownych przypadkach. W niektórych przypadkach może to być istotna różnica.
źródło
Tutaj napisałem o tym trochę:
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
Rozumiem twój punkt widzenia. I zgadzam się z tym, co mówi: operator rzutowania komunikuje „Jestem pewien, że ten obiekt można przekonwertować na ten typ i jestem gotów zaryzykować wyjątek, jeśli się mylę”, podczas gdy operator „as” komunikuje się „Nie jestem pewien, czy ten obiekt można przekonwertować na ten typ; jeśli mam rację, daj mi wartość zerową”.
Istnieje jednak subtelna różnica. (x jako T). Cokolwiek () komunikuje „Wiem nie tylko, że x można przekonwertować na T, ale ponadto, że robi to tylko konwersje odniesienia lub rozpakowania, a ponadto, że x nie jest zerowy”. To komunikuje inne informacje niż ((T) x). Cokolwiek () i być może właśnie tego zamierza autor kodu.
źródło
((T)x).Whatever()
również, żex
nie jest [zamierzone] na zero, i bardzo wątpię, aby autor zazwyczaj dbał o to, czy konwersjaT
nastąpi tylko z konwersjami referencyjnymi lub rozpakowanymi, czy też wymaga konwersji zdefiniowanej przez użytkownika lub zmieniającej reprezentację. W końcu, jeśli zdefiniujępublic static explicit operator Foo(Bar b){}
, to wyraźnie moją intencjęBar
należy uznać za zgodnąFoo
. Rzadko chciałbym uniknąć tej konwersji.Często widziałem odniesienia do tego wprowadzającego w błąd artykułu jako dowodu, że „as” jest szybsze niż casting.
Jednym z bardziej oczywistych wprowadzających w błąd aspektów tego artykułu jest grafika, która nie wskazuje na to, co jest mierzone: podejrzewam, że mierzy nieudane rzuty (gdzie „as” jest oczywiście znacznie szybsze, ponieważ nie jest zgłaszany żaden wyjątek).
Jeśli poświęcisz czas na wykonanie pomiarów, zobaczysz, że rzutowanie jest, jak można się spodziewać, szybsze niż „as”, gdy rzutowanie się powiedzie.
Podejrzewam, że może to być jeden z powodów użycia „kultowego ładunku” słowa kluczowego zamiast obsady.
źródło
Bezpośrednia obsada wymaga pary nawiasów więcej niż
as
słowa kluczowego. Więc nawet w przypadku, gdy masz 100% pewności, jaki jest typ, zmniejsza to bałagan wizualny.Zgodziłem się jednak co do wyjątku. Ale przynajmniej dla mnie większość zastosowań
as
sprowadza się do sprawdzenianull
później, co wydaje mi się lepsze niż wyłapanie wyjątku.źródło
99% czasu, kiedy używam „as”, ma miejsce, gdy nie jestem pewien, jaki jest rzeczywisty typ obiektu
i nie chcę wychwytywać jawnych wyjątków rzutowania ani podwójnie rzucać, używając „is”:
źródło
as
. Jaki jest pozostały 1%?To dlatego, że ludziom podoba się to, jak wygląda, jest bardzo czytelny.
Spójrzmy prawdzie w oczy: operator rzutowania / konwersji w językach podobnych do C jest dość straszny, jeśli chodzi o czytelność. Wolałbym, żeby C # przyjął albo składnię JavaScript:
Lub zdefiniuj
to
operator, będący odpowiednikiem rzutowaniaas
:źródło
dynamic_cast<>()
(i podobnych). Robisz coś brzydkiego, powinno to wyglądać brzydko.Ludzie
as
tak bardzo lubią , ponieważ dzięki temu czują się bezpieczni od wyjątków ... Jak gwarancja na pudełku. Facet kładzie na pudełku fantazyjną gwarancję, ponieważ chce, żebyś poczuł w sobie ciepło i ciepło. Myślisz, że włożyłeś w nocy to małe pudełko pod poduszkę, Wróżka Gwarancyjna może zejść i opuścić kwaterę, mam rację Ted?Wracając do tematu ... gdy używasz rzutowania bezpośredniego, istnieje możliwość wystąpienia niepoprawnego wyjątku rzutowania. Tak więc ludzie stosują się
as
jako ogólne rozwiązanie wszystkich swoich potrzeb castingowych, ponieważas
(samo w sobie) nigdy nie zgłasza wyjątku. Ale zabawną rzeczą jest to, że w przykładzie, który podałeś(x as T).SomeMethod();
, wymieniasz niepoprawny wyjątek rzutowania na wyjątek zerowy. Co zaciemnia prawdziwy problem, gdy widzisz wyjątek.Na ogół nie używam
as
zbyt wiele. Wolęis
test, ponieważ dla mnie wydaje się bardziej czytelny i ma więcej sensu niż próba obsady i sprawdzenie, czy nie ma nic.źródło
as
jako ogólne rozwiązanie, ponieważ zapewnia im poczucie bezpieczeństwa.To musi być jeden z moich najlepszych pomysłów .
D & E Stroustrup i / lub jakiś post na blogu, którego nie mogę teraz znaleźć, omawia pojęcie
to
operatora, który zająłby się kwestią przedstawioną przez https://stackoverflow.com/users/73070/johannes-rossel (tj. Taką samą składnią jakas
zDirectCast
semantyką ).Powodem, dla którego ta implementacja nie została wdrożona, jest to, że obsada powinna powodować ból i być brzydka, więc zostaniesz odepchnięty od korzystania z niej.
Szkoda, że „sprytni” programiści (często autorzy książek (Juval Lowy IIRC)) omijają to, nadużywając
as
w ten sposób (C ++ nie oferujeas
, prawdopodobnie z tego powodu).Nawet VB ma większą spójność posiadające jednolitą składnię, że zmusza do wyboru
TryCast
alboDirectCast
i uzupełnić swój umysł !źródło
DirectCast
zachowanie , a nie składnię .semantics
zamiast tego: Pdouble-to-int
rzutowania, który zawiódłby, gdybydouble
nie reprezentował dokładnej wartości, która mogłaby się zmieścić wInt32
, ale uzyskanie(int)-1.5
wydajności -1 jest po prostu brzydkie.MaybeValid<T>
dwa publiczne polaIsValid
iValue
który kod mógłby zrobić, jeśli uzna to za stosowne. To pozwoliłoby npMaybeValid<TValue> TryGetValue(TKey key) { var ret = default(MaybeValid<TValue>); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }
. Pozwoliłoby to nie tylko zaoszczędzić co najmniej dwie operacje kopiowania w porównaniu z nimiNullable<T>
, ale może również być przydatne w przypadku każdego typuT
- nie tylko klas.Uważam, że to
as
słowo kluczowe może być uważane za bardziej elegancką wersjędynamic_cast
C ++.źródło
dynamic_cast
do C ++.std::bad_cast
.static_cast
nie wykonuje żadnej kontroli typu środowiska wykonawczego. Nie ma podobnej obsady do tego w języku C #.Prawdopodobnie jest bardziej popularny bez powodu technicznego, ale dlatego, że jest łatwiejszy do odczytania i bardziej intuicyjny. (Nie mówię, że dzięki temu lepiej jest po prostu odpowiedzieć na pytanie)
źródło
Jednym z powodów używania „jako”:
Zamiast (zły kod):
źródło
obj
oznaczałaby zmianęobj
samej zmiennej, tak aby zawierała odwołanie do innego obiektu. Nie zmieniłoby to zawartości pamięci, w której znajduje się obiekt, do którego pierwotnie się odwołujeobj
. Ten oryginalny obiekt pozostałby niezmieniony, at
zmienna nadal zawierałaby odniesienie do niego.