Перевірка наявності об’єкта в C #


226

Я хотів би запобігти подальшій обробці об’єкта, якщо він є нульовим.

У наступному коді я перевіряю, чи об’єкт недійсний будь-яким:

if (!data.Equals(null))

і

if (data != null)

Однак я отримую NullReferenceExceptionат dataList.Add(data). Якщо об’єкт був недійсним, він ніколи не повинен був навіть входити до ifповідомлення!

Таким чином, я запитую, чи це правильний спосіб перевірити, чи об’єкт недійсний:

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;
}

Якщо це правильний спосіб перевірити, чи об’єкт недійсний, що я роблю неправильно (як я можу запобігти подальшій обробці об’єкта, щоб уникнути NullReferenceException)?


13
Ви також повинні використовувати throw e;протиthrow new Exception(e.ToString());
Nix

17
у C # ви завжди повинні використовуватись != nullу своїх нульових чеках. .Equalsзавжди буде кидати виняток, якщо об’єкт є нульовим.
Кайл Трауберман

42
@Nix: throw e;не набагато краще. throw;, з іншого боку ...
Jon

4
@developer: e.ToString()створить рядок, що включає не тільки повідомлення про помилку, але й всі з них InnerExceptionsі слід стека. Отже, це якесь дуже жирне повідомлення про виключення. Якщо ви (справедливо!) Хочете зберегти цю інформацію та зберігати там, де вона належить, використовуйте просто throw;.
Джон

14
Спроба / улов наразі нічого не робить. Усі кажуть, що просто використовуйте "кидок", але якщо ви нічого не робите за винятком, а повторно кидаєте його, то навіщо взагалі мати блок "спробувати / ловити"? Зазвичай ви збираєте винятки, щоб витончено обробити їх, очистити ресурси (краще за допомогою пункту "нарешті") або зробити якийсь журнал, перш ніж повторно викидати виняток. Нічого з цього не відбувається в цьому коді, тому немає потреби в спробі / лові взагалі.
Девід Петерсон

Відповіді:


252

Справа не в dataтому null, але dataList.

Вам потрібно створити його

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

Ще краще: оскільки це поле, зробіть це private. І якщо вам нічого не заважає, зробіть і це readonly. Просто хороша практика.

Убік

Правильний спосіб перевірити наявність недійсності - це if(data != null). Цей вид перевірки є повсюдним для референтних типів; навіть Nullable<T>перевизначає оператор рівності, щоб бути більш зручним способом вираження nullable.HasValueпри перевірці на недійсність.

Якщо ви зробите це, if(!data.Equals(null))ви отримаєте NullReferenceExceptionif data == null. Цілком весело, оскільки уникнення цього винятку було метою, в першу чергу.

Ви також робите це:

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

Це, безумовно, не добре. Я можу собі уявити, що ви помістили його туди просто, щоб ви могли прорватися до налагоджувача, перебуваючи всередині методу, і в цьому випадку ігноруйте цей параграф. Інакше ні за що не ловіть винятки. А якщо це зробити, викиньте їх, використовуючи просто throw;.


5
Для цієї мети я бачив також Object.ReferenceEquals (obj, null) . Чи варто уникати відміни рівності?
Лука

2
@LucaPiccioni Я використовував його , щоб запобігти значення типу-скаржиться при використанні дженериків: geekality.net/2009/11/13/generics-and-checking-for-null
Svish

4
Я віддаю перевагу null != data. Введення константи спочатку перетворює друкарську помилку null = dataна помилку компілятора, а не на ненавмисне призначення. (Також працює для ==.)
jpmc26

6
@ jpmc26: У C # if (data = null)вже є помилка часу компіляції, тому навіть якщо на те, щоб потрапити туди, потрібні десятиліття, нам більше не потрібно стежити за цим. Навіть компілятори C ++ легко створюють попередження про можливе ненавмисне призначення цього коду.
Джон

Лука, ви також можете уникнути відміни рівності, відкинувши "об’єкт" у тесті. У подібній думці ця відповідь повинна замість цього стверджувати: "if ((object) data! = Null)", оскільки це дозволяє уникнути помилок, коли рівність була скасована.
DAG

81

в C #> 7.0 використання

if (obj is null) ...

Це буде ігнорувати будь-який == або! = Визначений об'єктом (якщо, звичайно, ви не хочете їх використовувати ...)

Для недійсного використання if (obj is object)(або if (!(obj is null)))


1
Цікаво, чи існує "не нульовий"? (python би сказав obj is not null)
1818

1
Чому це краще, ніж якщо (obj! = Null), що є більш читабельним
Orn Kristjansson

38
if (obj aint null)
Нік Булл

10
Бо не нульовим єif (obj is object)
яцковський

3
@OrnKristjansson, тому що! = І == можна змінити.
mitchellJ

61

C # 6 має монадичну перевірку нуля :)

перед:

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

після:

var bestValue = points?.FirstOrDefault()?.X ?? -1;

7
Тому що "коментарі можуть редагуватися лише протягом 5 хвилин"? Що? Так чи інакше ... Коли я добирався до цього ... Я прийшов сюди в пошуках кращого синтаксису для висловлення, result = myObject == null ? null : myObject.SomePropertyі ваш приклад підказав мені написати result = myObject?.SomeProperty. Людина !! Це підлість. Я все ще люблю кодування ...
Адам Кокс

27

Ваш список даних є недійсним, оскільки він не був створений, судячи з опублікованого вами коду.

Спробуйте:

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;

}


3
Крім того, просто додати, якщо дані є недійсними, вони не будуть збійні, ви можете додати null до списку <Object>.
DaveShaw

7
Але спроба зробити .Equals на нуль би кинула виняток. Слід робити! = Null
glosrob

@glosrob: Ах !! Який нагляд! Я думав, що NullReferenceException від об’єкта .. а не з списку! Я новачок у c #, і я подумав, що існує спеціальний спосіб перевірки наявності null в c #!
розробник

Це теж, але я побачив, що Ед С. це прикрив.
DaveShaw

1
@DaveShaw: Дякую за голову вгору. Я хочу уникати додавання нульового об'єкта для подальшої обробки, тому все одно я перевірю. :)
розробник

19

[Відредаговано для відображення натяку на @ kelton52]

Найпростіший спосіб - це зробити object.ReferenceEquals(null, data)

Оскільки (null==data)НЕ гарантовано працює:

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));
}

Виробляє:

Порівнюючи '' з 'Nully'

Правда

помилковий


1
Насправді я просто спробував це, і зауваження "Мається на увазі перевага в тому, що він ігнорує будь-які зміни, які можуть бути присутніми в класі даних, наприклад" оператор! = "." Схоже, це не відповідає дійсності.
Келлі Елтон

9

Ні, ви повинні використовувати !=. Якщо dataнасправді це недійсно, то ваша програма просто вийде з ладу NullReferenceExceptionв результаті спроби викликати Equalsметод null. Також розумійте, що якщо ви спеціально хочете перевірити рівність еталону, вам слід скористатися Object.ReferenceEqualsметодом, оскільки ви ніколи не знаєте, як Equalsбуло реалізовано.

Ваша програма виходить з ладу, тому що вона dataListє нульовою, оскільки ви ніколи не ініціалізуєте її.


7

Проблема в цьому випадку не в тому, що dataце недійсне. Це те, що dataListсаме по собі є недійсним.

У місці, де ви заявляєте dataList, слід створити новий Listоб'єкт і призначити його змінній.

List<object> dataList = new List<object>();

5

На додаток до відповіді @Jose Ortega , його кращий для використання метод розширення

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

І використовувати IsNullметод для всіх об'єктів, таких як:

object foo = new object(); //or any object from any class
if (foo.IsNull())
   {
     // blah blah //
   }

1
Чому return T == null ? true : false;і не просто return T == null;?
md2perpe

1
Я не впевнений, що згоден. Дивно викликати метод на об’єкт, щоб перевірити, чи він недійсний. Не знаючи, що це метод розширення, ви могли б подумати, що це призведе до нульового виключення.
Джеймі Твіллс

Можна повністю підтвердити, що Джеймі вірно - це не вийде. Я знаю, тому що у мене був момент, що замислив зайця, і написав подібний метод розширення: P Код завжди кидав нульовий посилання на виняток, він абсолютно не перейде в метод розширення.
Джеймс Кінг

Насправді я хочу сказати, що ви можете зробити thatwith методом розширення ... може бути, у коду є якась проблема і може покращитися!
Алі

Ви можете викликати метод розширення на нульовому об'єкті; вам потрібно просто порівняти T (в даному випадку) з нульовим, щоб бути обережними. Джеймі прав, хоча це виглядає дивно.
Тім Баррас

3

Станом на C # 8 ви можете використовувати властивість "порожній" властивості (із узгодженням шаблону ), щоб переконатися, що об'єкт не є нульовим:

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

Такий підхід означає " якщо об'єкт посилається на екземпляр чогось " (тобто це недійсне).

Ви можете думати про це як протилежність: if (obj is null).... що поверне істину, коли об’єкт не посилається на екземпляр чогось.

Більш детальну інформацію про візерунки в C # 8.0 читайте тут .


3

Станом на C # 9 ви можете це зробити

if (obj is null) { ... }

Для недійсного використання

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

Якщо вам потрібно перекрити цю поведінку, використовуйте ==і !=відповідно.


2

Джефрі Л Вітлідж прав. Сам об’єкт `dataList '- недійсний.

Існує ще одна проблема з вашим кодом: Ви використовуєте ключове слово ref, що означає, що дані аргументу не можуть бути недійсними! MSDN говорить:

Аргумент, переданий параметру ref, повинен спочатку ініціалізуватися. Це відрізняється від зовнішніх, аргументи яких не повинні бути явно ініціалізовані перед їх передачею

Також не годиться використовувати дженерики типу "Об'єкт". Дженерики повинні уникати боксу / розпакування, а також забезпечувати безпеку типу. Якщо ви хочете загального типу, зробіть свій метод загальним. Нарешті ваш код повинен виглядати так:

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) {
        //...
      }
    }

2

Як і інші вже вказували, це не , dataа швидше за все , dataListщо це null. На додачу до цього...

catch- throwце антипаттерн, який майже завжди змушує мене кидати щоразу, коли я його бачу. Уявіть, що щось йде не так глибоко в щось, що doOtherStuff()дзвонить. Все, що ти повернеш, - це Exceptionпредмет, кинутий на throwв AddData(). Ні сліду стека, ні інформації про дзвінки, ні стану, ані нічого, що б вказувало на справжнє джерело проблеми, якщо ви не зайшли і не переключили налагоджувач, щоб перерватись на викинутий виняток, а не на виняток, не оброблений. Якщо ви ловите виняток і просто перекидаєте його будь-яким способом , особливо якщо код у блоці спробу будь-яким чином нетривіальний, зробіть собі (і вашим колегам, теперішнім та майбутнім) послугу і викиньте весь try- catchблок . Здійснено,throw;краще, ніж альтернативи, але ви все одно даєте собі (або хто ще намагається виправити помилку в коді) абсолютно непотрібні головні болі. Це не означає, що спроба уловлювання обов'язково сама по собі зла, доки ви робите щось актуальне за винятком об'єкта, який був кинутий всередині блоку лову.

Тоді Exceptionв першу чергу виникають потенційні проблеми з ловом , але це інша справа, тим більше, що в цьому конкретному випадку ви кидаєте виняток.

Ще одна річ, яка вважає мене більш ніж трохи небезпечною, - це те, що dataпотенційно може змінити значення під час виконання функції, оскільки ви проходите посилання. Таким чином, нульова перевірка може пройти, але перш ніж код приступить до чогось із значенням, це зміниться - можливо, на null. Я не впевнений, це турбота чи ні (це може бути не так), але, схоже, варто стежити за цим.


2
  public static bool isnull(object T)
  {
      return T == null ? true : false;
  }

використання:

isnull(object.check.it)

Умовне використання:

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

Оновлення (іншим способом) оновлено 31.08.2017. Дякуємо за коментар

public static bool isnull(object T)
{
    return T ? true : false;
}

5
cond ? true : false;абсолютно рівнозначний справедливому cond. Це нічого не додає.
lericson

Вибачте, але якщо ви перевірите функцію, вона повинна повернути значення bool. Я роблю формалізм. Тож перевірте це
Хосе Ортега

3
він означає return T == null;також повертає булевий!
MQoder

Я знаю, що він говорить. ty
Хосе Ортега

1
Замість return T == null ? true : false;просто використовувати return T == null;.
md2perpe

1

Щоразу, коли ви створюєте об'єкти класу, ви повинні перевірити, чи є об'єкт нульовим чи не використовуючи наведений нижче код.

Приклад: object1 - об’єкт класу

void myFunction(object1)
{
  if(object1!=null)
  {
     object1.value1 //If we miss the null check then here we get the Null Reference exception
  }
}

0

Я просто дотримувався методу, який ми зазвичай дотримувались у сценарії Java. Щоб перетворити об'єкт у рядок, а потім перевірити, чи є вони нульовими.

var obj = new Object();
var objStr = obj.ToString();
if (!string.IsNullOrEmpty(objStr)){
  // code as per your needs
}

0

Я зробив більш простий (позитивний спосіб) і, здається, працює добре.

Оскільки будь-який «об’єкт» - це принаймні об’єкт


    if (MyObj is Object)
    {
            //Do something .... for example:  
            if (MyObj is Button)
                MyObj.Enabled = true;
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.