Традиционный способ сделать так, чтобы внешний контейнер «юниверса» возвращал значение «ноль» для своего контейнера.Преимущество в том, что это легко.Недостатком является то, что вы не знаете, что прошли через край вселенной, пока не стало слишком поздно, чтобы вернуться.Как вы сказали в комментарии: использование «нуля» в качестве флага является слабым.
Два других решения, которые я видел, используются:
1) Объект юниверса - это собственный контейнер.Это имеет то преимущество, что ничто не является нулевым;у него есть недостаток, заключающийся в том, что при ходьбе по контейнерной цепи легко зайти в бесконечный цикл, и это не интуитивно понятно;вселенная на самом деле не содержит себя.По сути, вы используете равенство как флаг вместо обнуляемости как флаг;это тоже кажется слабым.
2) Объект юниверса выдает исключение, когда вы запрашиваете контейнер.Это вынуждает вызывающего вместо того, чтобы проверять наличие нулевого контейнера, вместо этого проверять, "Вы - вся вселенная?"прежде чем просить контейнер.То есть остановитесь, когда вы доберетесь до вершины, вместо того, чтобы остановиться, когда вы выйдете за верх.Это действительно хорошее решение, потому что оно заставляет людей писать защитный код.Вы не можете просто попросить контейнер, если не знаете, что он есть.Конечно, это требует, чтобы вызывающая сторона каким-то образом могла идентифицировать объект юниверса без проверки его родителя.Вам нужен "Я вся вселенная?"метод, или известный одноэлементный объект для сравнения, или какой-то другой механизм для определения того, какой контейнер является самым верхним.
Третий подход - отрицать предпосылку вопроса;Можно ли построить ваш тип данных таким образом, чтобы контейнер не был известен, или чтобы его знание было сведено к минимуму?
Например, в компиляторе, конечно, у нас много цепочек "контейнеров"ходить, и мы сигнализируем глобальное пространство имен, имея его содержащий символ быть нулевым (и тем, что он является известным одноэлементным объектом.) Но в большинстве случаев нам не нужно проверять, является ли родительский объект нулевым, потому чтовместо этого я пишу код, который создает абстракцию поверх него:
static IEnumerable<Container> AllContainers(this Thing thing)
{
if (thing == null) yield break;
Container current = thing.Container;
while(current != null)
{
yield return current;
current = current.Container;
}
}
Отлично.Теперь, когда у меня есть этот вспомогательный метод, мне больше не нужно проверять свойство Container какой-либо вещи.Если я хочу знать, «есть ли контейнер этой вещи, который содержит эту другую вещь?»тогда я могу сказать:
var query = from container in oneThing.AllContainers()
where container.Contains(otherThing)
select container;
bool result = query.Any();
Используйте мощь LINQ для перемещения деталей механистической реализации, таких как «как определить, когда я на вершине?»в высокоуровневые вспомогательные методы.Затем напишите свою логику на уровне «бизнеса», а не на уровне «механизма».