Czytałem o nowych funkcjach zmiennych wyjściowych w C # 7 tutaj . Mam dwa pytania:
To mówi
Zezwalamy również na „discards” jako parametry out, w postaci a
_
, aby pozwolić Ci zignorować parametry, które Cię nie interesują:p.GetCoordinates(out var x, out _); // I only care about x
P: Wydaje mi się, że to tylko informacja, a nie nowa funkcja języka C # 7, ponieważ możemy to zrobić również w C # 7.0:
var _; if (Int.TryParse(str, out _)) ...
czy coś mi tu brakuje?
Mój kod wyświetla błąd, gdy robię to, co wspomniano na tym samym blogu:
~Person() => names.TryRemove(id, out *);
*
nie jest prawidłowym identyfikatorem. Myślę, że przeoczenie Madsa Torgersena?
out _
_
nie jest zmienną, nie deklarujesz jej i nie możesz jej używać z nazwy. Wint _
tym jest zmienna.out _
bezvar
. Dziękivar
nim jest w istocie takie same jak poprzednio.Console.WriteLine(_)
- to się nie skompiluje, twierdząc, że nie ma takiej zmiennej. Całkiem dziwne. Co więcej: jeśli zrobisz coś takiego_ = SomeMethodCall()
- zostanie to zastąpione przez tylkoSomeMethodCall()
w skompilowanym kodzie. W końcu nadal nie możesz używać tej zmiennej w żadnym sensownym sensie.Odpowiedzi:
Odrzucenia , w C # 7 mogą być używane wszędzie tam, gdzie deklarowana jest zmienna, aby - jak sama nazwa wskazuje - odrzucić wynik. Zatem odrzucenie może być użyte bez zmiennych:
p.GetCoordinates(out var x, out _);
i można go użyć do odrzucenia wyniku wyrażenia:
_ = 42;
W przykładzie
p.GetCoordinates(out var x, out _); _ = 42;
Nie ma żadnej zmiennej
_
, która jest wprowadzana. Istnieją tylko dwa przypadki użycia odrzutu.Jeśli jednak
_
w zakresie istnieje identyfikator , nie można użyć odrzutów:var _ = 42; _ = "hello"; // error - a string cannot explicitly convert from string to int
Wyjątkiem jest sytuacja, gdy
_
zmienna jest używana jako zmienna wyjściowa. W tym przypadku kompilator ignoruje typ lubvar
i traktuje go jako odrzucenie:if (p.GetCoordinates(out double x, out double _)) { _ = "hello"; // works fine. Console.WriteLine(_); // error: _ doesn't exist in this context. }
Zauważ, że dzieje się tak tylko wtedy, gdy w tym przypadku jest używane
out var _
lubout double _
. Po prostu użyj,out _
a następnie jest traktowany jako odniesienie do istniejącej zmiennej_
, jeśli jest w zakresie, np:string _; int.TryParse("1", out _); // complains _ is of the wrong type
Wreszcie
*
notacja została zaproponowana na wczesnym etapie dyskusji na temat odrzutów, ale została porzucona na rzecz_
tego ostatniego, ponieważ jest to notacja częściej stosowana w innych językach .źródło
_
_ = 42
„odrzuca [s] wynik wyrażenia” jest mylące, ponieważ_ = 42
samo w sobie jest wyrażeniem z wartością42
, więc w rzeczywistości nie ma miejsca odrzucanie. Nadal istnieje różnica, ponieważ_ = 42;
jest to również stwierdzenie, podczas gdy42;
nie jest, co ma znaczenie w niektórych kontekstach._ = 42
nie pokazuje, jaki jest cel tego odrzucenia - tj. Kiedy musiałbyś "nie przechowywać" wyrażenia, ale i tak je ocenić, biorąc pod uwagę, jak zwykle możesz ocenić (nietrywialne , przydatne) wyrażenie dobrze, bez jego przechowywania. Sam nie mogę od razu wymyślić użytecznego przykładu (i nie wiem, czy taki istnieje, czy jest to tylko wynik konsekwentnej gramatyki).Innym przykładem Discard Operator
_
w C # 7 jest dopasowanie wzorca zmiennej typuobject
wswitch
instrukcji, która została ostatnio dodana w C # 7:Kod:
static void Main(string[] args) { object x = 6.4; switch (x) { case string _: Console.WriteLine("it is string"); break; case double _: Console.WriteLine("it is double"); break; case int _: Console.WriteLine("it is int"); break; default: Console.WriteLine("it is Unknown type"); break; } // end of main method }
Ten kod będzie pasował do typu i odrzuci zmienną przekazaną do
case ... _
.źródło
Dla bardziej zaciekawionych
Rozważmy następujący fragment
static void Main(string[] args) { //.... int a; int b; Test(out a, out b); Test(out _, out _); //.... } private static void Test(out int a, out int b) { //... }
Oto, co się dzieje:
... 13: int a; 14: int b; 15: 16: Test(out a, out b); 02340473 lea ecx,[ebp-40h] 02340476 lea edx,[ebp-44h] 02340479 call 02340040 0234047E nop 17: Test(out _, out _); 0234047F lea ecx,[ebp-48h] 02340482 lea edx,[ebp-4Ch] 02340485 call 02340040 0234048A nop ...
Jak widać za kulisami, te dwie rozmowy mają ten sam skutek.
Jak zauważył @ Servé Laurijssen, fajne jest to, że nie musisz wstępnie deklarować zmiennych, co jest przydatne, jeśli nie jesteś zainteresowany niektórymi wartościami.
źródło
Odnośnie pierwszego pytania
Nowością jest to, że nie musisz
_
już deklarować wewnątrz ani na zewnątrz wyrażenia i możesz po prostu wpisaćint.TryParse(s, out _);
Spróbuj zrobić to jedną linijką przed C # 7:
private void btnDialogOk_Click_1(object sender, RoutedEventArgs e) { DialogResult = int.TryParse(Answer, out _); }
źródło
SomeMethod(out _, out _, out three)
Ma 3 parametry wyjściowe, ale odrzucam pierwsze dwa bez konieczności tworzenia zmiennych, takich jakunused1, unused2
itp.if (SomeMethod(out _, out _, out _)) _ = 5;
Do kogo_
to się odnosi?_
zmiennej, nawet gdybyś użyłout var _
. Wygląda na to, że podkreślenie ma specjalną obudowę, aby odrzucić wynik.W C # 7.0 (Visual Studio 2017 około marca 2017) odrzucenia są obsługiwane w przydziałach w następujących kontekstach:
Inne przydatne uwagi
Prosty przykład: tutaj nie chcemy używać pierwszego i drugiego parametru i potrzebujemy tylko trzeciego parametru
Zaawansowany przykład w przypadku przełącznika, w którym zastosowano również nowoczesne dopasowanie wzorców obudowy przełącznika ( źródło )
switch (exception) { case ExceptionCustom exceptionCustom: //do something unique //... break; case OperationCanceledException _: //do something else here and we can also cast it //... break; default: logger?.Error(exception.Message, exception); //.. break;
}
źródło
var _; if (Int.TryParse(str, out _))
To nie to samo.
Twój kod dokonuje przypisania.
W C # 7.0 _ nie jest zmienną, informuje kompilator, aby odrzucił wartość
( chyba że zadeklarowałeś _ jako zmienną ... jeśli to zrobisz, zmienna jest używana zamiast symbolu odrzucenia)
Przykład: możesz użyć _ jako żądła i int w tej samej linii kodu :
string a; int b; Test(out a, out b); Test(out _, out _); //... void Test(out string a, out int b) { //... }
źródło