Avatar ·

Что такое NullReferenceException в C# и как её исправить?

Всем привет! Я новичок в C# и постоянно сталкиваюсь с ошибкой NullReferenceException. Пишу небольшое приложение для управления списком сотрудников, и при попытке вывести данные о человеке программа вылетает с этой ошибкой. Вот пример кода: ```csharp public class Employee { public string Name; public int Age; } class Program { static void Main() { Employee emp = null; Console.WriteLine(emp.Name); // Здесь ошибка! } } ``` Я понимаю, что переменная `emp` равна `null`, но не могу понять, почему это происходит и как правильно обрабатывать такие ситуации. Вроде бы всё просто: я создаю объект, но он пустой. Как мне избежать этой ошибки в будущем? Особенно когда работаю с коллекциями, базами данных или возвращаемыми значениями из методов. Подскажите, пожалуйста, как правильно проверять объекты на null и какие best practices существуют? Заранее спасибо! <<>>

Что такое NullReferenceException?

NullReferenceException (исключение нулевой ссылки) возникает, когда вы пытаетесь обратиться к члену объекта (свойству, методу, полю), который равен null. В C# ссылочные типы (классы, строки, массивы, делегаты) могут принимать значение null, означающее "отсутствие объекта". Попытка разыменовать null вызывает исключение.

Как исправить: базовые методы

1. Проверка на null через if

Самый простой способ — явная проверка перед использованием:

if (emp != null)
{
    Console.WriteLine(emp.Name);
}
else
{
    Console.WriteLine("Сотрудник не найден");
}

2. Оператор условного доступа (?.)

Начиная с C# 6.0, можно использовать оператор ?. (null-conditional operator). Он безопасно обращается к члену, и если объект null, возвращает null:

Console.WriteLine(emp?.Name); // Выведет пустую строку, если emp == null

Для методов: emp?.SomeMethod(); — метод не вызовется, если emp равен null.

3. Оператор объединения с null (??)

Позволяет задать значение по умолчанию, если объект null:

string name = emp?.Name ?? "Неизвестно";
Console.WriteLine(name); // Выведет "Неизвестно", если emp или Name равны null

4. Проверка аргументов методов

В начале методов проверяйте входящие параметры:

public void PrintEmployee(Employee emp)
{
    if (emp == null)
        throw new ArgumentNullException(nameof(emp));
    
    Console.WriteLine(emp.Name);
}

Продвинутые техники

Null-контекст (nullable reference types)

В C# 8.0+ можно включить nullable-контекст, компилятор будет предупреждать о потенциальных null-ссылках:

#nullable enable
Employee? emp = null; // Вопросительный знак означает, что может быть null
Console.WriteLine(emp!.Name); // Оператор ! подавляет предупреждение (dangerous!)
Console.WriteLine(emp?.Name); // Безопасно

Использование паттерна "Result object"

Вместо возврата null из методов, возвращайте специальный объект результата:

public class Result<T>
{
    public bool IsSuccess { get; }
    public T Value { get; }
    public string Error { get; }
    
    public static Result<T> Success(T value) => new Result<T> { IsSuccess = true, Value = value };
    public static Result<T> Failure(string error) => new Result<T> { IsSuccess = false, Error = error };
}

// Использование:
var result = FindEmployee(id);
if (result.IsSuccess)
    Console.WriteLine(result.Value.Name);
else
    Console.WriteLine($"Ошибка: {result.Error}");

Основные причины NullReferenceException

  • Неинициализированные поля — забыли присвоить значение объекту
  • Возврат null из методов/свойств — особенно из API или баз данных
  • Элементы коллекций — в массиве или списке могут быть null-элементы
  • Цепочки вызовов — obj?.Prop?.Method() без проверки

Отладка

При возникновении исключения Visual Studio подсветит строку. Используйте отладчик (F9 -> F5) и окно Watch, чтобы проверить, какая переменная равна null. Часто помогает разбить длинные цепочки вызовов на отдельные переменные.

Золотое правило: Если метод может вернуть null — документируйте это (через XML-комментарии или nullable-аннотации) и всегда проверяйте результат перед использованием.

<<>>

Альтернативный подход: Использование шаблона Null Object

Вместо постоянных проверок на null можно создать специальный "пустой" объект, который реализует то же поведение, но безопасно:

public class NullEmployee : Employee
{
    public override string Name => "Неизвестный сотрудник";
    public override int Age => 0;
}

// Вместо возврата null:
public Employee GetEmployee(int id)
{
    var emp = FindFromDatabase(id);
    return emp ?? new NullEmployee();
}

Этот паттерн особенно полезен, когда у вас много мест, где используется объект, и вы не хотите везде писать проверки. Минус — нужно создавать отдельный класс для каждого типа.

Логирование и мониторинг

Иногда null приходит из внешних источников (API, файлы, БД). Используйте глобальный обработчик исключений:

AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
    var ex = (Exception)args.ExceptionObject;
    Log.Error(ex, "Unhandled exception");
};

Это поможет не пропустить критичные ошибки, но не заменяет локальные проверки.

Совет: В современных проектах на C# старайтесь использовать nullable reference types и операторы ?./?? — это делает код чище и безопаснее. Но помните: никакая технология не заменит понимания того, какие переменные могут быть null.