Наследование данных в C # - PullRequest
3 голосов
/ 20 мая 2009

Существует ли известный шаблон для наследования данных в иерархической структуре объектов? У меня есть иерархическая структура «Элемент», которая должна наследовать свой «Тип» от своего «Родителя» (имеют те же данные, что и по умолчанию). Тип подчиненного элемента может быть изменен самостоятельно, и при изменении типа родительского элемента все дочерние элементы, тип которых не изменился, должны получить новый тип родительского элемента.

Обратите внимание, что я не могу подделать его как

public string Type
{
    get
    {
        if (type == null)
            return Parent != null ? Parent.Type : null;

        return type;
    }
}

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

Единственный способ, которым я могу думать об этом сейчас, это

public string Type
{
    set
    {
        type = value;
        UpdateUnchangedChildren(value);
    }
}

public int AddChild(Item item)
{
    item.Type = Type;
    return Items.Add(item);
}

Есть ли лучший способ? Спасибо.

Ответы [ 4 ]

3 голосов
/ 12 июня 2009

Это распространенная проблема, обычно связанная с обслуживанием различных иерархических настроек / конфигураций. Итак, я думаю, что решение этой проблемы можно считать «шаблоном».

В любом случае, с точки зрения внутренней архитектуры у вас есть 2 основных варианта:

  • нормализованная структура
  • денормализованная структура

«Нормазлед» - это тот, который реализован с помощью рекурсии. Определенный фрагмент данных всегда хранится в одном месте, все другие места имеют ссылки на него (например, на родительский элемент). Структура легко обновляется, но чтение из нее может быть проблемой.

«Денормализованный» означает, что каждый узел будет хранить весь набор настроек для своего уровня, и всякий раз, когда вы обновляете узел, требуется некоторое время, чтобы перейти вниз по иерархии и объединить все дочерние узлы. Но операция чтения происходит мгновенно.

И поэтому «денормализованная» версия кажется более широко используемой, потому что общий сценарий с настройками заключается в том, что вы обновляете их редко, а читаете их часто, поэтому вам нужна лучшая производительность чтения. Например, Модель безопасности Windows ACL использует «денормализованный» подход для быстрой проверки безопасности. Вы можете прочитать, как они разрешают конфликты между "унаследованными" и явными разрешениями (ACE), проверяя их в определенном порядке . Это может быть излишним для вашей конкретной системы, хотя вы можете просто указать, что определенное значение было переопределено, или, наоборот, сбросить на «по умолчанию» ...

Дальнейшие подробности зависят от потребностей вашей системы. Возможно, вам понадобится «гибридная» архитектура, в которой некоторые поля будут «нормализованы», а некоторые - нет. Но вы, кажется, на правильном пути.

1 голос
/ 12 июня 2009

Очевидной оптимизацией было бы кэширование значения, полученного от родителя при чтении типа. Это означает, что вы будете проходить каждый путь не более одного раза (тогда как наивное решение означает, что вы будете проходить каждый подпуть снова и снова для каждого содержащего его пути, что означает до O (h ^ 2) вместо O (h)) , Это прекрасно работает, если у вас больше операций чтения, чем записи.

Учтите это:

class Node
{
    string _cachedParentType = null;
    string _type;
    string Type 
    {
        get { return _type ?? _cachedParentType ?? (_cachedParentType = Parent.Type); }
        set 
        { 
            _type = value; 
            foreach (var child in Children) { child._cachedParentType = null; }
        }
    }
}

Это означает, что при достаточном количестве чтений и небольшом количестве записей чтение становится O (1) в лучшем случае, или, в худшем случае, «промах кеша» обойдется вам в O (h), где h - высота дерева; в то время как обновление - O (k) с k, являющимся уровнем ветвления (потому что мы обновляем только один слой вниз!). Я думаю, что это, как правило, будет лучше, чем решение UpdateUnchangedChildren (которое, как я полагаю, рекурсивно обновляет узлы вплоть до листьев), если только вы не выполняете WAY больше операций чтения, чем записи.

1 голос
/ 20 мая 2009

Я не уверен на 100%, что вы пытаетесь сделать ... но вы могли бы использовать обобщенные типы для передачи типа родительского объекта в дочерний объект ... Но наличие установщика там не совсем Имеет смысл ... Тип родительского объекта будет установлен, когда он будет создан, так зачем вам нужен сеттер для его изменения.

Предположим, у вас есть что-то вроде этого ...

public class Child<T>
{
   public string Type
   {
       get { return typeof(T).ToString(); }
   }
}

Итак, когда у вас есть Parent объект любого типа, вы можете передать его в свой дочерний объект ...

public class ParentA
{
   public Child<ParentA> ChildObj { get; set; }
}

public class ParentB
{
   public Child<ParentB> ChildObj { get; set; }
}

public class ParentC
{
   public Child<ParentC> ChildObj { get; set; }
}

Вызов любого из этих свойств ChildObj.Type вернет ParentA, ParentB и ParentC соответственно.

Buit У меня странное чувство, что вы не полностью объяснили, что вы пытаетесь сделать. Можете ли вы опубликовать еще несколько примеров кода, показывающих класс Parent и дочерний класс / элемент

0 голосов
/ 20 мая 2009

"... структура слишком глубока, чтобы использовать рекурсию и не беспокоиться о производительности."

Вы на самом деле измерили это? С какими предметами вы имеете дело, насколько глубока структура и как часто элементы не имеют собственного значения «Тип»? Каковы ваши цели производительности для приложения и как рекурсивное решение сравнивается с этими целями?

Люди часто думают, что рекурсия медленная, и поэтому исключают ее из рассмотрения, даже не пытаясь. НИКОГДА не рекомендуется отказываться от самого простого проекта по соображениям производительности, не измеряя его в первую очередь. В противном случае вы уйдете и придумаете более сложное решение, когда более простое сработало бы просто отлично.

Конечно, ваше второе решение также использует рекурсию, просто спускаясь по иерархии, а не вверх. Если дочерние вставки происходят в другое время и могут поглотить возможное снижение производительности, возможно, это будет более приемлемым.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...