Проверка на null значимых типов

  • Михаил
  • 8 мин. на прочтение
  • 171
  • 25 May 2024
  • 25 May 2024

В C#, когда вы пытаетесь привязать пустую строку ("") к свойству типа Guid, это приводит к ошибке модели состояния (model state error). Это связано с тем, что Guid является значимым типом, который не может быть напрямую привязан к пустой строке, тоже самое касается типов (например, int, bool, DateTime) в C# решить эту проблему можно несколькими способами:

1. Использование оператора ==:

  int? myInt = null;
  if (myInt == null)
  {
      Console.WriteLine("myInt is null");
  }
  else
  {
      Console.WriteLine("myInt is not null");
  }

Примечание: Для значимых типов необходимо использовать их nullable-версии (int?, bool?, DateTime?), чтобы они могли принимать значение null.

2. Использование оператора is:

  int? myInt = null;
  if (myInt is null)
  {
      Console.WriteLine("myInt is null");
  }
  else
  {
      Console.WriteLine("myInt is not null");
  }

Оператор is работает для любых типов, включая значимые типы.

3. Использование метода HasValue:

  int? myInt = null;
  if (myInt.HasValue)
  {
      Console.WriteLine($"myInt is not null, value is {myInt.Value}");
  }
  else
  {
      Console.WriteLine("myInt is null");
  }

Метод HasValue проверяет, имеет ли nullable-тип значение, отличное от null.

4. Использование оператора ?. (null-conditional operator):

  int? myInt = null;
  Console.WriteLine(myInt?.ToString()); // Output: null

Оператор ?. позволяет безопасно обращаться к членам nullable-типа (например, свойствам или методам) без возникновения исключения NullReferenceException, если сам nullable-тип равен null.

5. Использование оператора ?? (null-coalescing operator):

  int? myInt = null;
  int result = myInt ?? 0;
  Console.WriteLine(result); // Output: 0

Оператор ?? возвращает левый операнд, если он не равен null, в противном случае возвращает правый операнд. Это позволяет предоставлять резервные значения, когда nullable-тип равен null.

Важно отметить, что для значимых типов необходимо использовать их nullable-версии (int?, bool?, DateTime?), чтобы они могли принимать значение null. Работа с nullable-типами позволяет избежать возникновения исключений NullReferenceException при работе со значимыми типами. 

Относительно значимого типа Guid, есть несколько способов решить эту проблему:

6. Привязать пустую строку к Guid.Empty:

Вы можете создать специальный байндер, который будет привязывать пустую строку к значению Guid.Empty. Вот пример:

  public class GuidModelBinder : IModelBinder
  {
      public Task BindModelAsync(ModelBindingContext bindingContext)
      {
          if (bindingContext.ValueProvider.GetValue(bindingContext.ModelName).FirstValue == "")
          {
              bindingContext.Result = ModelBindingResult.Success(Guid.Empty);
              return Task.CompletedTask;
          }
          return DefaultModelBinder.BindModelAsync(bindingContext);
      }
  }

  // Регистрация байндера

  services.AddMvc(options =>
  {
      options.ModelBinderProviders.Insert(0, new GuidModelBinderProvider());
  });


В этом примере реализуется пользовательский IModelBinder, который проверяет, является ли значение пустой строкой. Если это так, он возвращает Guid.Empty вместо возникновения ошибки модели состояния.

7. Использовать аннотацию [BindNever]:

Вместо привязки пустой строки к Guid.Empty, вы можете пометить свойство типа Guid аннотацией [BindNever], чтобы избежать ошибки модели состояния:

  public class MyModel
  {
      public string Name { get; set; }
      [BindNever]
      public Guid Id { get; set; }
  }

Это предотвратит привязку пустой строки к свойству Id, и вместо этого оно сохранит значение по умолчанию для Guid.

8. Использовать [FromQuery] или [FromForm]:

Если вы не хотите использовать специальный байндер или аннотацию [BindNever], вы можете использовать атрибуты [FromQuery] или [FromForm], чтобы указать, что свойство должно быть привязано из запроса или формы, а не из модели:

  public class MyModel
  {
      public string Name { get; set; }
      [FromQuery]
      public Guid Id { get; set; }
  }

Это предотвратит автоматическую привязку пустой строки к свойству Id и вместо этого будет использовать значение, предоставленное в запросе.

Выбор наиболее подходящего подхода зависит от ваших конкретных требований и архитектурных решений. Использование пользовательского байндера может быть наиболее гибким подходом, позволяющим контролировать процесс привязки для различных типов данных.