2009-04-15 24 views

Odpowiedz

14

Pola są automatycznie inicjalizowane na logiczne zero dla typu; to jest ukryte. Zmienne muszą być zgodne z "określonym przydziałem", więc muszą być przypisane, zanim będą mogły zostać odczytane.

ECMA 334v4

§17.4.4 inicjalizacji Pole

Początkowa wartość pola, czy być statyczne pole lub pola przykład, wartość domyślna (§12.2) z typu pola. Nie jest możliwe, aby obserwować wartość pola przed wystąpiła domyślna inicjalizacja , a zatem pole nie jest nigdy "niezainicjalizowane".

i

§12. Zmienne

... Zmienna musi być definitywnie przypisana (§ 12.3) przed uzyskaniem jej wartości . ...

+0

To tylko powtarzając pytanie. Ale dlaczego? –

+0

@YairHalberstadt zależy od tego, czy "dlaczego?" oznacza regułę lub przyczynę reguły. Odpowiedziałem na pierwszy, który prawdopodobnie odpowiada na pytanie; dla drugiego: ponieważ do czasu, gdy rozważyłeś łańcuchy konstruktorów, metody wirtualne wywoływane podczas budowy oraz fakt, że na poziomie IL konstruktorzy bazowi mogą być wywoływani w dowolnym punkcie łańcucha wywoływania - prawie niemożliwe jest wypowiedzenie sensible o inicjalizacji pola; jednakowo, pola mogą się pokrywać, co czyni jeszcze ważniejszym zerowanie przestrzeni, więc nie jest wymagany technicznie init - kontrast ... –

+0

@YairHalberstadt zmienne lokalne, które mają bardzo łatwe sprawdzanie przydziału, gdzie niezainicjowane zwykle oznaczają błąd i gdzie może pominąć zerowanie (chociaż obecny czas wykonywania nigdy nie ma, IIRC). –

4

To nie powinno. Twój błąd powinien znajdować się w drugim wierszu, a nie w pierwszym, i powinien być, ponieważ używałeś go zanim go zainicjowałeś.

Kompilator pomaga tutaj.

Więc nie zainicjuj ich jako nawyku, zamiast tego pozwól kompilatorowi pomóc!

Fajną rzeczą jest to, że sprawdzi to dla ciebie. Jeśli posiadasz instrukcję switch z 3 przypadkami, gdzie każdy ustawia wartość, ale zapomnisz ustawić ją w swoim "domyślnym", ale użyjesz go, a następnie ostrzeże cię, że przeoczyłeś ścieżkę.

Jeśli zainicjujesz zmienne na = 0, usuniesz tę korzyść.

+0

Użyłem też test1 przed zainicjowaniem go, ale to było w porządku –

+0

Tak, to samo sprawdzanie nie jest pomocne w przypadku zmiennych klasowych - nie ma sposobu na śledzenie tych ścieżek, chyba że je wykonasz, wtedy należy upewnić się, że są wypełnione w konstruktorze (dobry pomysł, gdzie to możliwe) –

+0

Chociaż jego sprawdzanie ścieżki nie jest zbyt fantazyjne. Np .: załóżmy, że masz coś takiego: 'bool a, b = true; jeśli (b) a = prawda; bool c = a; '. Trzecie polecenie ** spowoduje ** wygenerowanie błędu, mimo że my, ludzie, wyraźnie widzimy, że do tego czasu zainicjujemy 'a' ** **. Kompilator nie sprawdza się w warunkach wewnętrznych - nawet jeśli są one tak proste i zawsze prawdziwe. (Dotyczy to również bardziej skomplikowanych przypadków). – Sushi271

2

Jak wskazuje Marc, tak mówi specyfikacja. Powodem, dla którego jest to dobre, jest to, że istnieją pewne ważne powody, aby pozostawić członka niezainicjowanego, a nie zmienną lokalną, której żywotność jest ograniczona przez metodę, w której się znajduje. Głównie chciałbyś tego tylko ze względu na wydajność, jeśli Zmienna jest kosztowna do zainicjowania i powinna być inicjowana tylko w określonych scenariuszach użycia. Z mojej strony uniknęłbym niezainicjowanych członków, dopóki moje plecy nie byłyby naprawdę pod ścianą!

W przypadku zmiennych lokalnych znacznie łatwiej jest wykryć, czy wszystkie ścieżki kodowe prawdopodobnie doprowadzą do zainicjowania, podczas gdy nie ma dobrej heurystyki, aby określić, czy wszystkie ścieżki kodu w całym programie gwarantują inicjalizację przed użyciem. Zupełnie poprawną odpowiedzią jest impossible in both cases, o czym powinni wiedzieć wszyscy uczniowie CS.

12

Rozszerzenie odpowiedzi Marka, inicjowanie zmiennej lokalnej jest również związane z procesem weryfikacji .
Interfejs CLI wymaga, aby w każdym możliwym do zweryfikowania kodzie (tj. Moduły, które nie zostały jawnie poproszone o pominięcie procesu weryfikacji przy użyciu właściwości SkipVerfication z atrybutu SecurityPermission), wszystkie lokalne zmienne muszą zostać zainicjowane przed ich użyciem. Nieprzestrzeganie tego spowoduje zgłoszenie VerficationException.

Co ciekawsze, kompilator automatycznie dodaje flagę .locals init do każdej metody wykorzystującej zmienne lokalne. Ta flaga powoduje, że kompilator JIT generuje kod, który inicjuje wszystkie zmienne lokalne do ich wartości domyślnych. To znaczy, mimo że już zainicjowałeś je we własnym kodzie, JIT będzie zgodny z flagą .locals init i wygeneruje odpowiedni kod inicjujący. Ta "zduplikowana inicjalizacja" nie ma wpływu na wydajność, ponieważ w konfiguracjach, które umożliwiają optymalizacje, kompilator JIT wykryje duplikację i skutecznie potraktuje ją jako "martwy kod" (automatycznie wygenerowana procedura inicjalizacji nie pojawi się w wygenerowanych instrukcjach asemblera).

Według Microsoftu (również poparte przez Erica Lipperta w odpowiedzi na pytanie na jego blogu), w większości przypadków, gdy programiści nie inicjują zmiennej lokalnej, nie robią tego, ponieważ przekazują na podstawowym środowiskiem do zainicjowania ich zmiennej, aby były wartościami domyślnymi, ale tylko dlatego, że "zapomniały", powodując czasami iluzoryczne błędy logiczne.
Aby zmniejszyć prawdopodobieństwo występowania błędów tego typu w kodzie C#, kompilator wciąż nalega, aby zainicjować zmienne lokalne. Nawet jeśli dodamy flagę .locals init do wygenerowanego kodu IL.

Bardziej wyczerpujące wyjaśnienie na ten temat można znaleźć tutaj: Behind The .locals init Flag

Powiązane problemy