Sprawdzanie, czy obiekt jest pusty w C #

226

Chciałbym zapobiec dalszemu przetwarzaniu obiektu, jeśli jest on pusty.

W poniższym kodzie sprawdzam, czy obiekt jest pusty przez:

if (!data.Equals(null))

i

if (data != null)

Jednak mogę otrzymać NullReferenceExceptionna dataList.Add(data). Jeśli obiekt był zerowy, nigdy nie powinien był nawet wejść do if-statement!

Dlatego pytam, czy to jest właściwy sposób sprawdzenia, czy obiekt jest pusty:

public List<Object> dataList;
public  bool AddData(ref Object data)
    bool success = false;
    try
    {
        // I've also used "if (data != null)" which hasn't worked either
        if (!data.Equals(null))
        {
           //NullReferenceException occurs here ...
           dataList.Add(data);
           success = doOtherStuff(data);
        }
    }
    catch (Exception e)
    {
        throw new Exception(e.ToString());
    }
    return success;
}

Jeśli jest to właściwy sposób sprawdzania, czy obiekt ma wartość null, co robię źle (jak mogę zapobiec dalszemu przetwarzaniu obiektu, aby uniknąć wyjątku NullReferenceException)?

deweloper
źródło
13
Powinieneś także użyć throw e;versusthrow new Exception(e.ToString());
Nix
17
w C # zawsze powinieneś używać != nullw swoich zerowych czekach. .Equalszawsze zgłasza wyjątek, jeśli obiekt jest pusty.
Kyle Trauberman,
42
@Nix: throw e;nie jest dużo lepszy. throw;, z drugiej strony ...
Jon
4
@developer: e.ToString()wygeneruje ciąg znaków, który zawiera nie tylko komunikat o błędzie, ale także wszystkie InnerExceptionsi ślad stosu. To rodzaj bardzo grubego komunikatu wyjątku. Jeśli (słusznie!) Chcesz zachować te informacje i zachować je tam, gdzie należy, użyj po prostu throw;.
Jon
14
Try / catch w tej chwili nic nie robi. Wszyscy mówią, po prostu używaj „rzucania”, ale jeśli nie robisz nic z wyjątkiem, ale rzucasz go ponownie, to po co blokować try / catch? Zwykle wychwytujesz wyjątki, aby z wdziękiem sobie z nimi poradzić, posprzątać zasoby (lepiej z klauzulą ​​„w końcu”) lub wykonać jakieś logowanie przed ponownym zgłoszeniem wyjątku. Żaden z nich nie występuje w tym kodzie, więc nie trzeba w ogóle próbować / łapać.
David Peterson

Odpowiedzi:

252

To nie datatak null, ale dataList.

Musisz go utworzyć

public List<Object> dataList = new List<Object>();

Jeszcze lepiej: skoro to pole, zrób to private. A jeśli nic Cię nie powstrzyma, zrób to również readonly. Po prostu dobra praktyka.

Na bok

Prawidłowy sposób sprawdzenia nieważności to if(data != null). Ten rodzaj kontroli jest wszechobecny dla typów referencyjnych; nawet Nullable<T>zastępuje operator równości, aby był wygodniejszym sposobem wyrażania nullable.HasValuepodczas sprawdzania nieważności.

Jeśli to zrobisz if(!data.Equals(null)), otrzymasz NullReferenceExceptionif data == null. Co jest dość komiczne, ponieważ unikanie tego wyjątku było celem przede wszystkim.

Robisz to również:

catch (Exception e)
{
    throw new Exception(e.ToString());
}

To zdecydowanie nie jest dobre. Mogę sobie wyobrazić, że umieściłeś go tam tylko po to, abyś mógł włamać się do debuggera, wciąż będąc w metodzie, w którym to przypadku zignoruj ​​ten akapit. W przeciwnym razie nie wychwytuj wyjątków za darmo. A jeśli to zrobisz, rzuć je ponownie za pomocą just throw;.

Jon
źródło
5
W tym celu widziałem także Object.ReferenceEquals (obj, null) . Czy ma to na celu ominięcie równości?
Luca,
2
@LucaPiccioni Użyłem go, aby zapobiec reklamacjom typu wartości podczas używania leków generycznych: geekality.net/2009/11/13/generics-and-checking-for-null
Svish
4
Wolę null != data. Wprowadzenie stałej jako pierwszej zamienia literówkę null = dataw błąd kompilatora, a nie niezamierzone przypisanie. (Działa również dla ==.)
jpmc26
6
@ jpmc26: W języku C # if (data = null)występuje już błąd czasu kompilacji, więc nawet jeśli dotarcie tam zajęło dekady, tak naprawdę nie musimy już na to uważać. Nawet kompilatory C ++ z łatwością wygenerują ostrzeżenie o możliwym niezamierzonym przypisaniu tego kodu.
Jon
Luca, możesz także uniknąć przesłonięcia równości, rzutując na „obiekt” w teście. Podobnie, w odpowiedzi ta powinna brzmieć następująco: „if ((object) data! = Null)”, ponieważ pozwala uniknąć błędów, gdy równość została nadpisana.
DAG,
81

w użyciu C #> 7.0

if (obj is null) ...

To zignoruje dowolne == lub! = Zdefiniowane przez obiekt (chyba że oczywiście chcesz ich użyć ...)

Do użytku nie zerowego if (obj is object)(lub if (!(obj is null)))

Kofifus
źródło
1
Zastanawiam się, czy istnieje „nie jest zero”? (powiedziałby python obj is not null)
sehe
1
Dlaczego to jest lepsze niż if (obj! = Null), co jest bardziej czytelne
Orn Kristjansson
38
Szkoda, że ​​nie wdrożą if (obj aint null):(
Nick Bull
9
Bo nie jest zeroweif (obj is object)
yatskovsky
3
@OrnKristjansson, ponieważ! = I == można zastąpić.
mitchellJ
61

C # 6 ma monadyczne sprawdzanie wartości zerowej :)

przed:

if (points != null) {
    var next = points.FirstOrDefault();
    if (next != null && next.X != null) return next.X;
}   
return -1;

po:

var bestValue = points?.FirstOrDefault()?.X ?? -1;
Jowen
źródło
7
Ponieważ „komentarze można edytować tylko przez 5 minut”? Co? W każdym razie ... Gdy się do tego zbliżałem ... Przyszedłem tutaj w poszukiwaniu lepszej składni do wyrażenia, result = myObject == null ? null : myObject.SomePropertya twój przykład dał mi wskazówkę do napisania result = myObject?.SomeProperty. Człowiek!! To podstępne. Nadal uwielbiam kodować ...
Adam Cox,
27

Twoja dataList ma wartość NULL, ponieważ nie została utworzona instancja, sądząc po opublikowanym kodzie.

Próbować:

public List<Object> dataList = new List<Object>();
public  bool AddData(ref Object data)
bool success = false;
try
{
    if (!data.Equals(null))   // I've also used if(data != null) which hasn't worked either
    {
       dataList.Add(data);                      //NullReferenceException occurs here
       success = doOtherStuff(data);
    }
}
catch (Exception e)
{
    throw new Exception(e.ToString());
}
return success;

}

glosrob
źródło
3
Ponadto, aby dodać, jeśli dane mają wartość NULL, nie ulegnie awarii, możesz dodać wartość NULL do listy <Obiekt>.
DaveShaw
7
Ale próba zrobienia .Equals na wartość NULL rzuci wyjątek. Powinieneś zrobić! = Null
glosrob
@glosrob: Ah !! Co za przeoczenie! Myślałem, że wyjątek NullReferenceException pochodzi z obiektu .. nie z listy! Jestem nowy na c # i pomyślałem, że istnieje specjalny sposób sprawdzania wartości null na c #!
programista
To też, ale widziałem, że Ed S. to omówił.
DaveShaw
1
@DaveShaw: Dzięki za zgłoszenie się. Chcę jednak uniknąć dodawania pustego obiektu do późniejszego przetworzenia, więc nadal sprawdzam. :)
programista
19

[Edytowane w celu odzwierciedlenia podpowiedzi @ kelton52]

Najprostszy sposób to zrobić object.ReferenceEquals(null, data)

Ponieważ (null==data)NIE gwarantuje się działania:

class Nully
{
    public static bool operator ==(Nully n, object o)
    {
        Console.WriteLine("Comparing '" + n + "' with '" + o + "'");
        return true;
    }
    public static bool operator !=(Nully n, object o) { return !(n==o); }
}
void Main()
{
    var data = new Nully();
    Console.WriteLine(null == data);
    Console.WriteLine(object.ReferenceEquals(null, data));
}

Produkuje:

Porównywanie „” z „Nully”

Prawdziwe

Fałszywe

gatopeich
źródło
1
Właściwie właśnie to wypróbowałem, a uwaga „Sugerowana zaleta polega na tym, że ignoruje wszelkie przesłonięcia, które mogą występować w klasie danych, takie jak„ operator! = ”. Nie wydaje się to prawdą.
Kelly Elton,
9

Nie, powinieneś używać !=. Jeśli datafaktycznie jest zerowy, to twój program po prostu zawiesi się NullReferenceExceptionw wyniku próby wywołania Equalsmetody null. Pamiętaj również, że jeśli chcesz sprawdzić równość referencji, powinieneś użyć tej Object.ReferenceEqualsmetody, ponieważ nigdy nie wiesz, jak Equalszostała zaimplementowana.

Twój program ulega awarii, ponieważ dataListjest zerowy, ponieważ nigdy go nie inicjujesz.

Ed S.
źródło
7

Problem w tym przypadku nie datapolega na tym, że jest zerowy. To, że dataListsam jest null.

W miejscu, w którym deklarujesz dataList, powinieneś utworzyć nowy Listobiekt i przypisać go do zmiennej.

List<object> dataList = new List<object>();
Jeffrey L. Whitledge
źródło
5

Oprócz odpowiedzi @Jose Ortega , jej lepsza metoda rozszerzenia wykorzystania

 public static bool IsNull(this object T)
     {
        return T == null;
     } 

I użyj IsNullmetody dla wszystkich obiektów, takich jak:

object foo = new object(); //or any object from any class
if (foo.IsNull())
   {
     // blah blah //
   }
Ali
źródło
1
Dlaczego return T == null ? true : false;nie tylko return T == null;?
md2perpe
1
Nie jestem pewien, czy się zgadzam. Dziwnie jest wywoływać metodę na obiekcie, aby sprawdzić, czy jest on pusty. Nie wiedząc, że jest to metoda rozszerzenia, można by pomyśleć, że spowoduje zgłoszenie wyjątku zerowego odniesienia.
Jamie Twells
Może całkowicie potwierdzić, że Jamie ma rację - to nie zadziała. Wiem, ponieważ miałem moment na zając i napisałem podobną metodę rozszerzenia: P Kod zawsze zgłaszał wyjątek zerowy odniesienia, absolutnie nie przejdzie do metody rozszerzenia.
James King
Faktycznie chcę powiedzieć, że możesz to zrobić za pomocą metody rozszerzenia ... może być problem z kodem i może ulec poprawie!
Ali
Możesz wywołać metodę rozszerzenia na obiekcie zerowym; musisz tylko porównać T (w tym przypadku) z wartością null, aby zachować ostrożność. Jamie ma rację, wygląda dziwnie.
Tim Barrass,
3

Od wersji C # 8 możesz użyć wzorca właściwości „pusta” (z dopasowaniem wzorca ), aby upewnić się, że obiekt nie ma wartości zerowej:

if (obj is { })
{
    // 'obj' is not null here
}

To podejście oznacza „ jeśli obiekt odwołuje się do wystąpienia czegoś ” (tzn. Nie ma wartości null).

Możesz to traktować jako przeciwieństwo: if (obj is null)... . która zwróci true, gdy obiekt nie odwołuje się do instancji czegoś.

Aby uzyskać więcej informacji o wzorach w C # 8.0, przeczytaj tutaj .

Darren Ruane
źródło
3

Od wersji C # 9 możesz to zrobić

if (obj is null) { ... }

Do użytku nie zerowego

if (obj is not object) { ... }

Jeśli musisz zastąpić to zachowanie, użyj ==i !=odpowiednio.

Eugene Chybisov
źródło
2

Jeffrey L Whitledge ma rację. Sam obiekt `dataList´ ma wartość NULL.

Istnieje również inny problem z twoim kodem: używasz słowa kluczowego ref, co oznacza, że ​​dane argumentu nie mogą mieć wartości null! MSDN mówi:

Argument przekazany do parametru ref musi zostać najpierw zainicjowany. Różni się to od naszego, którego argumentów nie trzeba jawnie inicjować przed ich przekazaniem

Nie jest również dobrym pomysłem stosowanie rodzajów ogólnych z typem „Obiekt”. Leki ogólne powinny unikać boksowania / rozpakowywania, a także zapewniać bezpieczeństwo typu. Jeśli chcesz mieć wspólny typ, uczyń swoją metodę ogólną. Wreszcie twój kod powinien wyglądać następująco:

public class Foo<T> where T : MyTypeOrInterface {

      public List<T> dataList = new List<T>();

      public bool AddData(ref T data) {
        bool success = false;
        try {
          dataList.Add(data);                   
          success = doOtherStuff(data);
        } catch (Exception e) {
          throw new Exception(e.ToString());
        }
        return success;
      }

      private bool doOtherStuff(T data) {
        //...
      }
    }
DiableNoir
źródło
2

Jak inni już wspomniano, nie jest to datajednak dość prawdopodobne, dataListże jest null. W dodatku...

catch- throwjest antypatternem, który prawie zawsze sprawia, że ​​chcę wymiotować za każdym razem, gdy go widzę. Wyobraź sobie, że coś idzie nie tak głęboko w czymś, co doOtherStuff()wzywa. Wszystko wrócisz to Exceptionprzedmiot, rzucony na throwIN AddData(). Brak śladu stosu, brak informacji o wywołaniu, brak stanu, nic, co wskazywałoby na prawdziwe źródło problemu, chyba że wejdziesz i przełączysz debugger na przerwanie zgłoszonego wyjątku zamiast wyjątku nieobsługiwanego. Jeśli wychwytujesz wyjątek i po prostu ponownie go rzucasz w jakikolwiek sposób , szczególnie jeśli kod w bloku try jest w jakikolwiek sposób nieistotny, zrób sobie (i swoim kolegom, obecnym i przyszłym) przysługę i wyrzuć całośćtry - catchblok . Zgoda,throw;jest lepsze niż alternatywy, ale wciąż dajesz sobie (lub ktokolwiek inny próbuje naprawić błąd w kodzie) zupełnie niepotrzebne bóle głowy. Nie oznacza to, że rzut próbny jest koniecznie zły sam w sobie, o ile zrobisz coś istotnego z rzuconym obiektem wyjątku do bloku catch.

Potem są potencjalne problemy z łapaniem Exception , ale to inna sprawa, zwłaszcza, że ​​w tym konkretnym przypadku rzucasz wyjątek.

Kolejną rzeczą, która wydaje mi się bardziej niż trochę niebezpieczna, jest to, że datamoże potencjalnie zmienić wartość podczas wykonywania funkcji, ponieważ przechodzisz przez referencję. Kontrola zerowa może więc zakończyć się pomyślnie, ale zanim kod zacznie cokolwiek robić z wartością, zostanie zmieniona - być może na null. Nie jestem pewien, czy jest to problem, czy nie (może nie być), ale wydaje się, że warto na niego uważać.

CVn
źródło
2
  public static bool isnull(object T)
  {
      return T == null ? true : false;
  }

posługiwać się:

isnull(object.check.it)

Zastosowanie warunkowe:

isnull(object.check.it) ? DoWhenItsTrue : DoWhenItsFalse;

Aktualizacja (inna metoda) zaktualizowana 31.08.2017. Dziękuję za komentarz.

public static bool isnull(object T)
{
    return T ? true : false;
}
Jose Ortega
źródło
5
cond ? true : false;jest całkowicie równoważne z just cond. To nic nie dodaje.
lericson
Przykro mi, ale jeśli zaznaczysz funkcję, musi ona zwrócić wartość bool. Robię formalizm. Więc sprawdź to jeszcze raz
Jose Ortega
3
ma na myśli return T == null;również zwraca wartość logiczną!
MQoder,
Wiem co on mówi. ty
Jose Ortega
1
Zamiast po return T == null ? true : false;prostu użyć return T == null;.
md2perpe
1

Za każdym razem, gdy tworzysz obiekty klasy, musisz sprawdzić, czy obiekt jest pusty, czy nie, używając poniższego kodu.

Przykład: obiekt1 jest obiektem klasy

void myFunction(object1)
{
  if(object1!=null)
  {
     object1.value1 //If we miss the null check then here we get the Null Reference exception
  }
}
użytkownik3483639
źródło
0

Właśnie zastosowałem metodę, którą zwykle stosujemy w skrypcie Java. Aby przekonwertować obiekt na ciąg, a następnie sprawdź, czy są one puste.

var obj = new Object();
var objStr = obj.ToString();
if (!string.IsNullOrEmpty(objStr)){
  // code as per your needs
}
Mohan Stanky
źródło
0

Zrobiłem prostszy (pozytywny sposób) i wydaje się, że działa dobrze.

Ponieważ jakikolwiek „obiekt” jest co najmniej przedmiotem


    if (MyObj is Object)
    {
            //Do something .... for example:  
            if (MyObj is Button)
                MyObj.Enabled = true;
    }
Gilles5678
źródło