代码之家  ›  专栏  ›  技术社区  ›  reticent

C中的数据继承#

  •  3
  • reticent  · 技术社区  · 15 年前

    是否有已知的模式来继承层次对象结构中的数据?我有一个分层的“item”结构,需要从它的“parent”继承它的“type”(与默认值具有相同的数据)。子项的类型可以自行修改,当父项的类型发生变化时,其类型未发生变化的所有子项都应得到新的父项类型。

    注意我不能假装

    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 回复  |  直到 15 年前
        1
  •  3
  •   Massimiliano    15 年前

    这是一个常见的问题,通常与维护各种层次设置/配置有关。所以,我想解决这个问题的方法可以被认为是“一种模式”。

    无论如何,从内部架构的角度来看,您有两个主要选项:

    • 归一化结构
    • 非规范化结构

    “normazlied”是用递归实现的。一个特定的数据总是存储在一个地方,所有其他地方都有对它的引用(例如,对父级的引用)。结构很容易更新,但从中读取可能是一个问题。

    “非规范化”意味着每个节点都将存储其级别的整个设置集,并且每当更新节点时,都需要一些时间才能进入层次结构并恢复所有子节点。但阅读操作是即时的。

    因此,“非规范化”版本似乎得到了更广泛的使用,因为使用设置的常见情况是,您很少更新它们,而经常读取它们,因此需要更好的读取性能。例如, Windows ACL security model 使用“非规范化”方法快速进行安全检查。你可以阅读他们 resolve conflicts between the "inherited" and explicit permissions (ACEs) by checking them in a specific order .对于您的特定系统来说,这可能是一个过度杀伤力,但是您可以简单地拥有一个标记,该标记将覆盖特定的值,或者相反,将其重置为“默认”…

    更多的细节取决于您的系统需求,您可能不需要一个“混合”架构,其中一些字段将被“规范化”,而另一些字段则不会。但您似乎走的是正确的道路。

        2
  •  1
  •   Eoin Campbell    15 年前

    我不完全确定你想做什么…但可以使用泛型将父对象的类型传递给子对象…但是在那里有一个二传手并没有真正意义…父对象的类型将在实例化时设置,所以为什么要在那里有一个setter来更改它。

    假设你有这样的东西…

    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。

    别说,我有一种有趣的感觉,你还没有完全解释你想做什么。 您能再发布一些显示父类和子类/项类的代码示例吗?

        3
  •  1
  •   Avish    15 年前

    一个明显的优化是在读取类型时缓存从父级获取的值。这意味着您最多只能遍历每个路径一次(而幼稚的解决方案意味着您将对包含它的每个路径一次又一次地遍历每个子路径,这意味着最多只能遍历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是分支级别(因为我们只更新下一层!).I认为这通常会比updateUnchangedChildren解决方案(我假定它会递归地更新节点,一直到leafs),除非您的读取比写入更多。

        4
  •  0
  •   David Nelson    15 年前

    “…结构太深,无法使用递归,因此不必担心性能。”

    你真的量过这个吗?您要处理的项目有多少,结构有多深,项目没有自己的“类型”值有多常见?应用程序的性能目标是什么,递归解决方案如何与这些目标进行比较?

    人们通常认为递归很慢,因此不必尝试就将其从考虑中消除。出于性能原因拒绝最简单的设计,而不首先对其进行测量,这绝对不是一个好主意。否则,你就去发明一个更复杂的解决方案,而简单的解决方案就可以了。

    当然,第二个解决方案也使用递归,只是沿着层次结构向下,而不是向上。如果孩子的插入发生在不同的时间,并且能够吸收可能的性能冲击,那么也许这将是更可接受的。