Dlaczego często widzi się „null! = Zmienna” zamiast „zmienna! = Null” w C #?


103

Czy w języku c # jest jakaś różnica w szybkości wykonania w kolejności, w której określasz stan?

if (null != variable) ...
if (variable != null) ...

Od niedawna pierwszy z nich widziałem dość często i przykuł moją uwagę, bo do drugiego przywykłem.

Jeśli nie ma różnicy, jaka jest zaleta pierwszego?



Niektóre języki programowania obsługują inicjalizację wartości w warunku if. W takich językach, jeśli chcemy porównać LValue z RValue, dobrą praktyką jest użycie literału po lewej stronie, np. If (1 == i). więc przypadkowo, jeśli wstawimy = raczej ==, to daje to błąd kompilatora.
Jugal Panchal

Odpowiedzi:


161

Jest to wstrzymanie ze strony C. W C, jeśli używasz złego kompilatora lub nie masz wystarczająco wysokich ostrzeżeń, skompilujesz się bez żadnego ostrzeżenia (i rzeczywiście jest to legalny kod):

// Probably wrong
if (x = 5)

kiedy prawdopodobnie miałeś na myśli

if (x == 5)

Możesz obejść ten problem w C, wykonując:

if (5 == x)

Literówka tutaj spowoduje nieprawidłowy kod.

Teraz w C # to wszystko jest bzdury. O ile nie porównujesz dwóch wartości logicznych (co jest rzadkie, IME), możesz napisać bardziej czytelny kod, ponieważ instrukcja „if” wymaga wyrażenia boolowskiego na początku, a typ „ x=5” to Int32nie Boolean.

Proponuję, aby jeśli zobaczysz to w kodzie swoich kolegów, wyedukuj ich w zakresie języków współczesnych i zasugeruj, aby w przyszłości pisali bardziej naturalną formę.


6
Wszyscy moi koledzy doprowadzają mnie tym do szału i chcą nawiasów klamrowych nawet dla pojedynczego stwierdzenia po if ... (na wypadek, gdybyś dodał coś później „nie zdając sobie sprawy”). Włącz F # i Boo (i YAML), jeśli twój język jest nieczytelny bez wcięć, uczyń go częścią języka, mówię.
Benjol

48
Dodanie nawiasów klamrowych jest rozsądne, IMO - C # z pewnością nie próbuje powstrzymać tego przed problemem. To bardzo różni się od problemu „stała == zmienna”.
Jon Skeet

6
a jeśli (to! = tamto) {performAsSuch (); } jest właściwie całkiem zdrowy. Argument „późniejszego dodawania” wydaje mi się tak kruchy, jak nie unikanie / zauważanie literówek, jeśli (a = 5)
jpinto3912

3
@jpinto: Nie jestem pewien, co próbujesz tu powiedzieć - że zrobić jak szelki wokół pojedynczych wypowiedzi, albo że nie? Różnica między tym a zmienną biznesową w pytaniu polega na tym, że w C # kod z literówką jest nieprawidłowym kodem, więc kompilator go zatrzyma. To samo nie dotyczy nie zakładania aparatu ortodontycznego.
Jon Skeet

3
@Benjol Nie zapomnij zawsze podawać pustą else { }klauzulę na wypadek, gdyby ktoś musiał coś tam dodać później i zapomniał samodzielnie wpisać elsesłowo kluczowe. (Żart)
Jeppe Stig Nielsen

11

Istnieje dobry powód, aby najpierw użyć wartości null: if(null == myDuck)

Jeśli class Ducknadpisujesz ==operator, if(myDuck == null)możesz przejść do nieskończonej pętli.

Użycie jako nullpierwsze powoduje użycie domyślnego komparatora równości i faktycznie robi to, co zamierzałeś.

(Słyszałem, że w końcu przyzwyczaisz się do czytania kodu napisanego w ten sposób - po prostu jeszcze nie doświadczyłem tej transformacji).

Oto przykład:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}

11
To jest źle. Głosowano w dół. Po prostu ustaw kursor myszy nad ==operatorem i umieść go tam, a zobaczysz, że w obu przypadkach używane jest przeciążenie zdefiniowane przez użytkownika. Oczywiście może to zrobić subtelną różnicę, jeśli sprawdzisz awcześniej, ba następnie zamieniasz pozycję az prawego operandu na lewy operand. Ale możesz też sprawdzić bwcześniej a, a wtedy b == nullbyłby ten, który odwrócił kolejność. To wszystko jest mylące, gdy operator dzwoni sam. Zamiast tego rzutuj operand (lub oba), objectaby uzyskać żądane przeciążenie. Like if ((object)a == null)lub if (a == (object)null).
Jeppe Stig Nielsen

10

Jak wszyscy już zauważyli, pochodzi on mniej więcej z języka C, w którym można uzyskać fałszywy kod, jeśli przypadkowo zapomnisz o drugim znaku równości. Ale jest jeszcze jeden powód, który pasuje również do C #: czytelność.

Weźmy po prostu ten prosty przykład:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

Jeśli po prostu zamieniasz wszystkie puste słowa na początek, możesz znacznie łatwiej wykryć wszystkie sprawdzenia:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

Więc ten przykład jest może złym przykładem (patrz wytyczne dotyczące kodowania), ale pomyśl tylko o szybkim przewijaniu całego pliku kodu. Po prostu widząc wzór

if(null ...

od razu wiesz, co będzie dalej.

Jeśli byłoby odwrotnie, zawsze musisz skanować do końca wiersza, aby zobaczyć sprawdzenie nieważności, pozwalając tylko na chwilę potknięcia się, aby dowiedzieć się, jaki rodzaj czeku jest tam wykonywany. Może więc podświetlanie składni może ci pomóc, ale zawsze jesteś wolniejszy, gdy te słowa kluczowe znajdują się na końcu linii, a nie na początku.


9

Myślę, że to programista C, który zmienił języki.

W C możesz napisać:

int i = 0;
if (i = 1)
{
    ...
}

Zwróć uwagę na użycie w tym miejscu pojedynczego znaku równości, co oznacza, że ​​kod przypisze 1 do zmiennej i, a następnie zwróci 1 (przypisanie jest wyrażeniem) i użyje 1 w instrukcji if, która będzie traktowana jako prawda. Innymi słowy, powyższe jest błędem.

Jednak w C # nie jest to możliwe. Rzeczywiście nie ma między nimi różnicy.


1
„Jednak w C # nie jest to możliwe” bez narzekania kompilatora, ponieważ instrukcja if wymaga wyrażenia boolowskiego, a podane jest typu int.
Robert Harvey

4

Wcześniej ludzie zapominali znak „!” (lub dodatkowe „=” dla równości, które jest trudniejsze do wykrycia) i wykonaj zadanie zamiast porównania. umieszczenie wartości null na początku eliminuje możliwość wystąpienia błędu, ponieważ null nie jest wartością l (tj. nie można jej przypisać).

Większość współczesnych kompilatorów wyświetla ostrzeżenie, gdy wykonujesz przypisanie warunkowe w dzisiejszych czasach, a C # faktycznie wyświetla błąd. Większość ludzi po prostu trzyma się schematu var == null, ponieważ dla niektórych jest on łatwiejszy do odczytania.


Wszystkie kompilatory C # (zauważ, że jest to pytanie C #) nie powinny skompilować instrukcji „if”, w której wyrażenie nie jest wartością logiczną ...
Jon Skeet,

4

Nie widzę żadnej korzyści z przestrzegania tej konwencji. W C, gdzie nie istnieją typy boolowskie, warto pisać

if( 5 == variable)

zamiast

if (variable == 5)

ponieważ jeśli zapomnisz o jednym ze znaków równości, skończysz z

if (variable = 5)

która przypisuje 5 zmiennej i zawsze daje wartość true. Ale w Javie wartość logiczna to wartość logiczna. A z! = W ogóle nie ma powodu.

Dobra rada to jednak pisać

if (CONSTANT.equals(myString))

zamiast

if (myString.equals(CONSTANT))

ponieważ pomaga uniknąć wyjątków NullPointerExceptions.

Radziłbym poprosić o uzasadnienie reguły. Jeśli nie ma, po co go śledzić? To nie poprawia czytelności


0

Dla mnie zawsze był to, jaki styl wolisz

@Shy - Z drugiej strony, jeśli pomylisz operatorów, powinieneś chcieć uzyskać błąd kompilacji lub będziesz uruchamiać kod z błędem - błąd, który wraca i gryzie cię później, ponieważ powoduje nieoczekiwane zachowanie


Nie sądzę, żebym kiedykolwiek słyszał o kimś, kto preferowałby formę „stała == zmienna” inaczej niż ze względów bezpieczeństwa, które są już omówione w C #.
Jon Skeet

Sam wolę "zmienna == stała", ale widziałem programistów, którzy przyjęli odwrócenie, ponieważ uważają, że jest to dla nich bardziej naturalne.
TheCodeJunkie

1
Czy wszyscy byli ludźmi z wieloletnim praniem mózgu C, czy był to prawdziwy wybór?
Jon Skeet

0

Jak wielu zauważyło, głównie w starym kodzie C był używany do identyfikacji błędu kompilacji, ponieważ kompilator zaakceptował to jako legalne

Nowy język programowania, taki jak java, go, jest wystarczająco inteligentny, aby wychwycić takie błędy kompilacji

Nie należy używać warunków w kodzie „null! = Variable”, ponieważ jest to bardzo nieczytelne


-4

Jeszcze jedno ... Jeśli porównujesz zmienną ze stałą (liczbą całkowitą lub ciągiem na przykład), umieszczenie stałej po lewej stronie jest dobrą praktyką, ponieważ nigdy nie napotkasz NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

natomiast

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

Ponieważ domyślnie C # instancjonuje wszystkie zmienne, nie powinieneś mieć tego problemu w tym języku.


1
Nie jestem pewien, jakiego kompilatora używasz dla pierwszego bitu kodu, ale powinien się skompilować (generalnie z ostrzeżeniem). Wartość i jest niezdefiniowana, ale ma PEWNĄ wartość.
Dolphin

Oczywiście oba kody będą się kompilować! To jest właśnie problem. Spróbuj uruchomić pierwszy kod, a zrozumiesz, co mam na myśli, mówiąc o „wyjątku zgłoszonym” ...
koni

nie ma też różnicy między nimi. Czy możesz rozwinąć?
mfazekas

Nie ma wyjątku, chyba że zadeklarujesz int * i w C / C ++, a nawet w tym przypadku porówna wskaźniki i nie zgłosi żadnego wyjątku ... Tak jak Dolphin stwierdził, że wartość jest niezdefiniowana, ale nie wygeneruje żadnych wyjątków.
Cobusve

bez wyjątków, powiedzmy, że ijest wartością losową bez odpowiedniej inicjalizacji. wyrażenie ma całkowicie tę samą semantykę w c / c ++
Raffaello
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.