Приведи пример проблемы, возникающей при прямом обращении к полям объекта, и способы её решения

«Приведи пример проблемы, возникающей при прямом обращении к полям объекта, и способы её решения» — вопрос из категории ООП, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Прямое обращение к публичным полям (public fields) или свойствам объекта без предварительной валидации его состояния — частая причина ошибок времени выполнения, главным образом NullReferenceException.

Пример проблемы:

public class UserProfile
{
    // Публичное поле — плохая практика.
    public Address HomeAddress;
}

public class Address
{
    public string City { get; set; }
}

// Клиентский код
var user = new UserProfile();
// Поле HomeAddress не инициализировано, по умолчанию null.
Console.WriteLine(user.HomeAddress.City); // NullReferenceException!

Решения и best practices:

  1. Инкапсуляция и использование свойств: Замена публичных полей на свойства с контролем доступа.

    public class UserProfile
    {
        private Address _homeAddress;
        public Address HomeAddress
        {
            get => _homeAddress;
            set => _homeAddress = value ?? throw new ArgumentNullException(nameof(value));
        }
    }
  2. Ленивая инициализация: Инициализация поля при первом обращении.

    public class UserProfile
    {
        private Address _homeAddress;
        public Address HomeAddress
        {
            get
            {
                if (_homeAddress == null)
                    _homeAddress = new Address(); // Или загрузить из БД
                return _homeAddress;
            }
        }
    }
  3. Проверка в клиентском коде (защитное программирование):

    // Явная проверка
    if (user?.HomeAddress != null)
    {
        Console.WriteLine(user.HomeAddress.City);
    }
    // Использование null-условных операторов (C# 6+)
    Console.WriteLine(user?.HomeAddress?.City ?? "Address not set");
  4. Использование паттерна Null Object: Возвращать специальный, «пустой» объект вместо null.

    public class NullAddress : Address
    {
        public override string City => "[No City Specified]";
    }
    // В свойстве getter:
    get => _homeAddress ?? new NullAddress();
  5. Инициализация в конструкторе (наиболее предпочтительно): Гарантирует, что объект создается в валидном состоянии.

    public class UserProfile
    {
        public UserProfile(Address address)
        {
            HomeAddress = address ?? throw new ArgumentNullException(nameof(address));
        }
        public Address HomeAddress { get; } // Read-only property
    }
    // Использование
    var user = new UserProfile(new Address { City = "Moscow" });
    // HomeAddress никогда не будет null.

    Главный вывод: Публичные поля нарушают инкапсуляцию. Состояние объекта должно контролироваться самим объектом, а клиентский код должен быть защищен от работы с объектом в невалидном состоянии (например, с null-ссылками).