Как создать нулевой объект в С#
Мартин Фаулер Рефакторинг обсуждает создание Null Objects, чтобы избежать большого количества
if (myObject == null)
тесты.
Каков правильный способ сделать это? Моя попытка нарушает правило "виртуальный член в конструкторе".
Здесь моя попытка:
public class Animal
{
public virtual string Name { get; set; }
public virtual string Species { get; set; }
public virtual bool IsNull
{
get { return false; }
}
}
public sealed class NullAnimal : Animal
{
public override string Name
{
get{ return "NULL"; }
set { }
}
public override string Species
{
get { return "NULL"; }
set { }
}
public virtual bool IsNull
{
get { return true; }
}
}
Ответы
Ответ 1
Я согласен с ответом Wyatt Barnett в том, что вы должны проявлять сдержанность при создании таких "нулевых" объектов. Тем не менее, есть несколько хороших причин для этого. Иногда.
Я также согласен с ответом Supertux в том, что вся точка нулевого объекта не должна проверять, является ли оно нулевым, поэтому вы должны потерять свойство IsNull. Если вы действительно чувствуете, что вам нужно свойство IsNull, тогда снова прочитайте ответ Wyatt и передумайте.
И спасибо CraigTP за хорошие ссылки для получения дополнительной информации. Хороший материал.
Теперь я буду предполагать, что в вашем реальном коде у вас есть конструктор, который пытается установить значения Name или Species (независимо от того, что вы можете назвать реальным эквивалентом кода). В противном случае, почему вы получили предупреждение "ошибка виртуального участника в конструкторе"? Я столкнулся с несколькими подобными проблемами при использовании newfangled MyProperty {get; задавать; } shortcut (особенно, когда используется в структурах, и не заставляйте меня начинать сериализацию версий). Ваше решение состоит в том, чтобы не использовать ярлык, но вместо этого сделать это старомодным способом.
public class Animal {
protected Animal() { }
public Animal(string name, string species) {
_Name = name;
_Species = species;
}
public virtual string Name {
get { return _Name; }
set { _Name = value; }
}
private string _Name;
public virtual string Species {
get { return _Species; }
set { _Species = value; }
}
private string _Species;
}
public sealed class NullAnimal : Animal {
public override string Name {
get { return String.Empty; }
set { }
}
public override string Species {
get { return String.Empty; }
set { }
}
}
Это решает проблему установки ваших виртуальных свойств в конструкторе. Вместо этого вы устанавливаете свои личные значения полей (то, что вы не имеете возможности ссылаться, если вы используете ярлык). Для дополнительного кредита, скомпилируйте оба метода и используйте рефлектор, чтобы посмотреть результирующие сборки.
Чем больше я использую {get; задавать; } ярлык, тем больше мне это не нравится.
Ответ 2
Посмотрите на количество боли, которое интересующие концепции, такие как DbNull, вызвали и подумали, действительно ли это хорошая идея.
Protip: если вы постоянно проверяете наличие нулевых ссылок, вы, вероятно, должны немного переосмыслить API, чтобы исключить нулевые объекты ближе к вершине стека.
Protip II: что-то генерирует исключение, когда есть неожиданный нуль, на самом деле прекрасный и денди. Вещи должны идти бум, если у вас есть нули, где не должно быть null.
Ответ 3
Точка шаблона Null Object заключается в том, что для предотвращения сбоя или ошибки не требуется нулевая проверка.
Например, если вы попытались выполнить операцию над свойством Species и это было null - это вызовет ошибку.
Итак, вам не нужен метод isNull, просто верните что-то в getter, которое не приведет к сбою/ошибке приложения, например:
public class Animal
{
public virtual string Name { get; set; }
public virtual string Species { get; set; }
}
public sealed class NullAnimal : Animal
{
public override string Name
{
get{ return string.Empty; }
set { ; }
}
public override string Species
{
get { return string.Empty; }
set { ; }
}
}
Ответ 4
Пожалуйста, просмотрите эти ссылки как для самого шаблона проектирования (шаблон проектирования нулевого объекта), так и для реализации (на С#):
Шаблон дизайна:
Образец нулевого объекта - Википедия
Образец шаблона нулевого объекта
Внедрить нулевой объект (содержит некоторый код реализации)
Реализация:
Образец нулевого объекта
Ответ 5
Вы используете этот подход только в том случае, если это уместно. Ваш пример объекта Animal не может быть хорошим примером, потому что он не представляет подходящего случая, когда вы будете использовать этот подход. Например:
Animal animal = new Animal();
if (animal.tail == null)
{
//do nothing because wagging a tail that doesn't exist may crash the program
}
else
{
animal.wagTail();
}
В этом примере вы должны создать объект Animal, чтобы, если у животного нет хвоста, он может успешно обработать команду wagTail() без сбоев.
Class Animal
{
Tail tail;
void wagTail()
{
if (this.tail == null)
{
//do nothing
}
else
{
this.tail.doTheWag();
}
}
}
Теперь вам не нужно делать нулевую проверку, но может просто вызвать animal.wagTail() независимо от того, есть ли у животного хвост или нет.
Ответ 6
Я хотел бы упомянуть здесь несколько интересных деталей. Посмотрите на свой класс. Есть ли в ней какая-то логика? Это не класс в своем смысле, это структура данных. То, что вы пытаетесь сделать, это применить шаблон нулевого объекта к тому, к которому он не применим. Структуры данных ближе к типам значений, чем к классам. Там, где вы можете проверить нуль, вы можете решить эту проблему.
Образец нулевого объекта - это не то, за чем вам следует всегда следовать. Шаблон нулевого объекта - это то, что вы можете использовать, чтобы избежать нарушения принципа замещения Лискова, чтобы представлять класс, который ничего не делает, потому что null не является подходящей заменой для класса, поскольку это значение, но не класс.
Но все зависит от типов значений и структур данных. Нуль - это ценность! Поэтому в этом случае нулевая проверка - это правильная вещь.