Niedawno napisałem jakiś kod, w którym nieumyślnie ponownie użyłem nazwy zmiennej jako parametru akcji zadeklarowanej w funkcji, która już ma zmienną o tej samej nazwie. Na przykład:
var x = 1;
Action<int> myAction = (x) => { Console.WriteLine(x); };
Kiedy zauważyłem duplikację, zdziwiłem się, widząc, że kod został skompilowany i działał idealnie, czego nie oczekiwałbym na podstawie tego, co wiem o zakresie w języku C #. Kilka szybkich googlingów pojawiło się w pytaniach SO, narzekających, że podobny kod powoduje błąd, takich jak wyjaśnienie zakresu lambda . (Wkleiłem ten przykładowy kod do mojego IDE, aby sprawdzić, czy będzie on działał, aby się upewnić; działa idealnie). Dodatkowo, kiedy wchodzę do okna dialogowego Zmień nazwę w Visual Studio, pierwszy x
jest podświetlany jako konflikt nazw.
Dlaczego ten kod działa? Używam C # 8 z Visual Studio 2019.
x
parametr tej metody zostaje usunięty z zakresu. Zobacz na przykład sharplab .Odpowiedzi:
Odpowiedziałeś na swoje pytanie! To dlatego, że używasz C # 8.
Reguła od C # 1 do 7 brzmiała: prosta nazwa nie może oznaczać dwóch różnych rzeczy w tym samym zasięgu lokalnym. (Rzeczywista reguła była nieco bardziej skomplikowana, ale opisywała, jak to jest nużące; szczegółowe informacje można znaleźć w specyfikacji C #).
Celem tej reguły było zapobieżenie sytuacji, o której mówisz w swoim przykładzie, w której bardzo łatwo jest pomylić się co do znaczenia tego, co lokalne. W szczególności zasada ta została zaprojektowana w celu zapobiegania nieporozumieniom, takim jak:
A teraz mamy sytuację, w której wnętrze ciała oznacza zarówno lokalne
M
,x
jakthis.x
i lokalnex
.Mimo dobrych intencji z tą regułą było wiele problemów:
W przepisie Roslyn starałem się to rozwiązać; Dodałem kilka nowych komunikatów o błędach i sprawiłem, że stare były spójne pod względem miejsca zgłoszenia błędu. Jednak wysiłek ten był zbyt mały, zbyt późny.
Zespół C # zdecydował dla C # 8, że cała reguła powoduje więcej zamieszania niż zapobiega, i reguła została wycofana z języka. (Dzięki Jonathon Chase za ustalenie, kiedy nastąpiła emerytura.)
Jeśli chcesz poznać historię tego problemu i sposób, w jaki próbowałem go naprawić, zapoznaj się z artykułami, które o nim napisałem:
https://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/
https://ericlippert.com/2009/11/05/simple-names-are-not-so-simple-part-two/
https://ericlippert.com/2014/09/25/confusing-errors-for-a-confusing-feature-part-one/
https://ericlippert.com/2014/09/29/confusing-errors-for-a-confusing-feature-part-two/
https://ericlippert.com/2014/10/03/confusing-errors-for-a-confusing-feature-part-three/
Pod koniec trzeciej części zauważyłem, że istnieje także interakcja między tą funkcją a funkcją „Kolor koloru” - to znaczy funkcją, która pozwala:
W tym przypadku użyliśmy prostej nazwy
Color
w odniesieniu zarówno do, jakthis.Color
i do wyliczonego typuColor
; według ścisłego odczytania specyfikacji powinien to być błąd, ale w tym przypadku specyfikacja była niepoprawna, a intencją było zezwolenie na to, ponieważ kod ten jest jednoznaczny i denerwujące byłoby zmuszenie dewelopera do jego zmiany.Nigdy nie napisałem tego artykułu opisującego wszystkie dziwne interakcje między tymi dwiema regułami, i byłoby to teraz bezcelowe!
źródło