Dlaczego operator is zwraca false, gdy ma wartość null?


135

Wydaje mi się, że isoperator jest trochę niekonsekwentny.

bool Test()
{
    // Returns false, but should return true.
    return null is string;
}

Oczekuje się, że null wartość należy do dowolnego typu odwołania (lub dopuszcza wartość null). I rzeczywiście, specyfikacja języka C # mówi coś, co potwierdza tę hipotezę, na przykład (6.1.6 Niejawne konwersje referencji):

Niejawne konwersje referencyjne to:
...
• Od literału zerowego do dowolnego typu referencyjnego.

Opis (7.10.10 operator is) isoperatora zaczyna się od stwierdzenia, że ​​wyrażenie (E is T)da w wyniku wartość true, gdy istnieje konwersja odwołania z Ena T, ale następnie autorzy kontynuują, wyraźnie wykluczając przypadek, gdy Ejest to nullliterał lub ma nullwartość .

Dlaczego oni to robią? Wydaje mi się to sprzeczne z intuicją.


Dlaczego wyraźnie wykluczyli wartość null, to w najlepszym przypadku pytanie do programmers.stackoverflow.com
Miserable Variable

2
Nie mogę odwołać mojego bliskiego głosu, ale te pytania są w porządku tutaj: meta.stackexchange.com/questions/36850/…
Merlyn Morgan-Graham

w java null typeof Objectzwraca również false
ratchet freak

Załóżmy, że null is stringbyły true, a to sugeruje, że nulljest to plik string. Załóżmy również, że to null is Nullable<int>znaczy true, sugerując, że nulljest to plik Nullable<int>. A teraz odpowiedz na pytanie: jakiego typu jest null?
CVn

1
@Michael Kjörling: Non sequitur. Ze wspomnianych faktów (null to ciąg znaków, null to Nullable <int>) nie wynika, że ​​powinienem być w stanie odpowiedzieć na to pytanie.
Gebb

Odpowiedzi:


195

To było pytanie tematem mojego bloga 30 maja 2013 roku . Dzięki za świetne pytanie!


Patrzysz na pusty podjazd.

Ktoś pyta cię "czy twój podjazd może pomieścić Honda Civic?"

Tak. Tak, może.

Ktoś wskazuje na drugi podjazd. Jest również pusty. Pytają "Czy obecna zawartość mojego podjazdu zmieści się na twoim podjeździe?"

Tak, oczywiście. Oba podjazdy są puste! Tak więc wyraźnie zawartość jednego może zmieścić się w drugim, ponieważ na pierwszym miejscu nie ma zawartości żadnego.

Ktoś pyta: „Czy na Twoim podjeździe znajduje się Honda Civic?”

Nie.

Myślisz, że isoperator odpowiada na drugie pytanie: czy przy tej wartości mieści się ona w zmiennej tego typu? Czy zerowe odwołanie pasuje do zmiennej tego typu? Tak.

To nie jest pytanie, na które isodpowiada operator. Pytanie, na które isodpowiada operator, jest pytaniem trzecim. y is Xnie pyta „ jest ywartość prawną zmiennej typu X? ” pyta „ Czy yprawidłowym odwołaniem do obiektu typu X? ” Ponieważ odwołanie zerowy nie jest ważne odniesienie do dowolnego przedmiotu dowolnego typu, odpowiedź brzmi „nie ”. Ten podjazd jest pusty; nie zawiera Hondy Civic.

Innym sposobem spojrzenia na to jest y is Xodpowiedź na pytanie „czy gdybym powiedział y as X, czy uzyskałbym wynik inny niż zerowy? Jeśli y jest zerowe, najwyraźniej odpowiedź brzmi: nie!


Aby przyjrzeć się bliżej swojemu pytaniu:

Oczekuje się, że wartość null należy do dowolnego typu odwołania (lub dopuszcza wartość null)

Można by niejawnie założyć, że typ jest zbiorem wartości , a zgodność przypisania wartości y ze zmienną typu X to nic innego, jak sprawdzenie, czy y jest członkiem zbioru x .

Chociaż jest to niezwykle powszechny sposób patrzenia na typy, nie jest to jedyny sposób patrzenia na typy i nie jest to sposób, w jaki C # patrzy na typy. Odwołania o wartości null są elementami członkowskimi żadnego typu w języku C #; zgodność przypisania to nie tylko sprawdzanie zestawu, aby zobaczyć, czy zawiera on wartość. Tylko dlatego, że odniesienie zerowe jest przypisanie zgodne ze zmienną typu referencyjnego X, nie oznacza, że ​​null jest członkiem typu X. Relacje „jest przypisanie zgodne z” i relacja „jest członkiem typu” mają oczywiście wiele nakładają się, ale nie są identyczne w środowisku CLR.

Jeśli interesują Cię rozważania na temat teorii typów, zapoznaj się z moimi ostatnimi artykułami na ten temat:

Co to za rzecz, którą nazywasz „typem”? Część pierwsza

Co to za rzecz, którą nazywasz „typem”? Część druga


@Gebb: Cieszę się, że mogłem pomóc. Ten artykuł może Cię również zainteresować: blogs.msdn.com/b/ericlippert/archive/2010/09/16/…
Eric Lippert


y is Xnie pyta „czy jest ywartością prawną zmiennej typu X?” Pyta „Czy yto prawidłowe odniesienie do obiektu typu X?” O to właśnie chodziło! dzięki!
Jalal

Ale gdybym zapytał y is int?, to zapytałbym: „Czy na twoim podjeździe jest Honda Civic, czy nic?”. Dlaczego odpowiedź nadal brzmi „nie”, kiedy podjazd jest pusty? Jeśli spojrzysz pod maskę, sytuacja tylko się pogorszy, gdy pojawi się pytanie: „Czy na Twoim podjeździe znajduje się Honda Civic lub tabliczka z napisem„ Nie ma tu Hondy Civic ”?”. Nawet jeśli twój podjazd zawiera ten znak, odpowiedź nadal brzmi: nie.
Faks

1
@Gerard: Kiedy udzielam długich, wyczerpujących, gadatliwych odpowiedzi, ludzie mówią mi, że powinny być krótsze, a kiedy udzielam zwięzłych, lakonicznych odpowiedzi, ludzie mówią mi, że powinny być dłuższe. Mój wniosek jest taki: ludzie lubią narzekać. Ponieważ masz przeczucia co do tego, co stanowi dobrą odpowiedź na to ośmioletnie pytanie, proponuję, abyś pokazał nam, co jest dobrą odpowiedzią, odpowiadając na nie samodzielnie, abyśmy mogli się uczyć.
Eric Lippert

24

Myślę, że null is stringzwracanie fałszu jest bardzo intuicyjne. Null nic nie znaczy i na pewno nie jest ciągiem. Więc powinno zwrócić fałsz. Chociaż jest to wybór dokonany przez projektantów języka, jest on bardzo intuicyjny, gdy weźmie się pod uwagę znaczenie null w prawdziwym świecie.


2
+1, ponieważ tak naprawdę pytanie dotyczy tego, czy jest to intuicyjne, a nie kwestionowanie specyfikacji, o której OP jest już świadomy. Jeśli zapytasz je jako zdanie angielskie „to nullciąg znaków”, odpowiedź brzmi nie, nie jest, co różni się od „można nullprzypisać do string
Davy8

null is stringjest dość intuicyjny, (string)null is stringmoże wymagać przerwy, jeśli nie wiesz, jak działa odwołanie zerowe w C #.
gruPL

24

Plik nullDosłowna może być przypisany do dowolnego typu referencyjnego. To nie jest sam w sobie typ. Jest to specjalny literał, który reprezentuje odniesienie zerowe.

W przypadku, gdy ispowróci, truegdy nullzostanie przekazane a, co mógłbyś zrobić z nulldosłownym? Nic - to jest null. Jaki byłby sens tego, gdyby wrócił, truez wyjątkiem spraw zagmatwanych?


Niezależnie - jeśli chodzi o intuicyjność, przeczytaj kod po angielsku i powiedz mi:

null is string;

Kiedy to widzę, wydaje mi się, że zadaje to pytanie is "nothing" a string? . Moja intuicja podpowiada mi, że nie, nie jest - to jest nothing.


W porządku, ale jest to również ciąg, prawda? Ale dlaczego w takim razie (null is string)powraca?
Gebb

Sformułowałbym to na odwrót. Każdy typ odwołania może mieć wartość null. null jest po prostu zerowe.
Mark H

@Oded, więc czy wartość null jest prawidłową wartością, powiedzmy, typu obiektu? Mogę go przypisać do zmiennej tego typu, prawda? Więc wydaje się, że odpowiedź brzmi tak, wartość jest całkowicie poprawna i przypuszczam, że (null is object)wyrażenie powinno dokładnie to sprawdzić.
Gebb

@Gebb: Nie zapominaj, że ValueTypes to także obiekty, ale nie typy referencyjne.
Mark H

1
@Oded: „Jaki byłby sens przywrócenia prawdy, z wyjątkiem spraw zagmatwanych?” Właściwie uczyniłoby to język bardziej logicznym. Rozważ te dwa punkty: (1) isoperator sprawdza, czy istnieje konwersja odniesienia (nie potrzebujemy tutaj innych typów konwersji); (2) nullmożna przekonwertować na dowolny typ odniesienia. W związku z tym (null is T), gdy T jest typem referencyjnym, powinien zwrócić true. Ale nie, tutaj mamy ten wyraźny wyjątek, który wydaje mi się zagmatwany.
Gebb

12

http://msdn.microsoft.com/en-us/library/scekt9xw%28v=vs.71%29.aspx

Wyrażenie is przyjmuje wartość true, jeśli spełnione są oba poniższe warunki:

  • wyrażenie nie jest puste.
  • wyrażenie można rzutować na typ. Oznacza to, że wyrażenie rzutowania formularza (typ (wyrażenie) zakończy się bez zgłaszania wyjątku. Więcej informacji zawiera sekcja 7.6.6 Wyrażenia rzutowane.

1
W każdym razie dokumentacja stwierdza tylko, że wyrażenie nie może być zerowe (które już znamy), ale nie mówi dlaczego
nazwa użytkownika

Dlaczego chcesz tego w inny sposób?
Nędzna zmienna

1
@ nazwa_użytkownika: ta odpowiedź wyjaśnia, jak to działa mechanicznie, i podaje odniesienie, które daje model do pracy. Pytasz tutaj o znaczenie wszechświata. Chyba masz szczęście, że nasz bóg tak często bywa;)
Merlyn Morgan-Graham

10

W praktyce posiadanie „null to T == false” oszczędza mi wpisywania dodatkowego kodu:

Zamiast mówić

if (X != null && X is Foo) {}

Mogę tylko powiedzieć

if (X is Foo) {}

i skończ z tym.


8

nullwartość

Zacytowałem to z twojego pytania, ponieważ wydaje się, że dotarło do sedna sprawy. null nie jest wartością - to brak wartości. Celem isdla mnie wydaje się, aby odpowiedzieć na pytanie:

Jeśli rzucę Ena T, czy uda mi się uzyskać T?

Teraz, podczas gdy może rzucić nulldo T bezbłędnie , po zrobieniu więc nie „posiada T” - wciąż masz nic. Więc nie jest tak, że null„jest” a T, więc iszwraca fałsz.


3

W Javie jest operator, który robi dokładnie to samo, ale ma znacznie dłuższą nazwę: instanceof. Jest tam bardzo intuicyjny, który null instanceof Stringzwraca fałsz, ponieważ null nie jest instancją niczego, a tym bardziej a String. Tak więc podczas używanianull z wersji Java jest nieco bardziej intuicyjna.

Jednak oba te operatory zwracają wartość true, gdy są proszone o przejrzenie również całej hierarchii. Na przykład. jeśli wystąpienie Stringjest Object. I tu jest to Java, który jest nieco mniej intuicyjne (ponieważ wystąpienie rzeczywiście ma jedną, bardzo specyficzny rodzaj) i C # 's isjest bardziej intuicyjny (bo każdy String jestObject głęboko wewnątrz).

Konkluzja: jeśli spróbujesz opisać dość zaawansowaną logikę jednym słowem, na pewno niewielu ludzi będzie zdezorientowanych, w ten czy inny sposób. Wydaje się, że większość ludzi zgodziła się co do jednego znaczenia, a ci, którzy się nie zgadzali, musieli się dostosować.

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.