4

我在层次结构中有一个对象,其值可以默认为父对象的值。两个对象属于同一类型。

例如:如果Score属性是double.NaN,那么Score应该从Parent属性指向的对象中检索值,但前提Parent是设置了 ( Parent != null)。

我的问题是,我怎样才能以一种通用的方式可靠地实现这样的模型?我有 2 个选择,但也许还有更多?

选项 1:更改每个属性 getter 和 setter 以检查正在设置或获取的属性是否具有默认值,如果是,请尝试从父级读取它

    private double score = double.NaN;
    public double Score
    {
        get { return (score == double.NaN && Parent != null) ? Parent.Score : score; }
        set { score = (Parent != null && Parent.Score == value) ? double.NaN : value; }
    }

优点:

  • 分别对所有属性值进行实时原子更新
  • 默认值的显式比较

缺点:

  • 每个属性的getter和setter都需要手动实现,容易出错
  • 每次获取和设置都会影响性能

选项 2:实现对象加载和保存的默认设置

    void AfterLoad()
    {
        if(Parent != null)
        {
            if(score == double.NaN)
            {
                score = Parent.Score;
            }
            // (...)
        }
    }

    void BeforeSave()
    {
        if(Parent != null)
        {
            if(score == Parent.Score)
            {
                score = double.NaN;
            }
            // (...)
        }
    }

    void AfterSave()
    {
        AfterLoad();
    }

优点:

  • 仅在加载(一次)和保存(两次)时使用父关系,从而提高性能

缺点:

  • 任何时候父对象的改变都不会影响子属性

  • 在更改父对象之前和之后,需要对子对象进行处理(在父对象更改后回退到新的默认值)

  • 原子性可能会受到影响 - 在保存对象期间不能被任何其他线程访问

我相信很多人在实现对象“样式化”模型时都遇到过类似的困境。我正在寻找一个干净且有效的解决方案,它也可以将子对象和父对象的集合连接在一起(使用 CompositeCollection ?)。

4

3 回答 3

1

您可以使用 WPF 样式的方法,其中属性值不是存储在字段中,而是存储在基类的字典中。

这样所有属性看起来像

double Prop {
 get{ return (double)GetValue("Prop"); }
 set{ SetValue("Prop",value); }
}

如果没有为当前班级设置,GetValue 将检查所有父母以找到默认值。

于 2013-04-29T13:31:18.963 回答
1

我正在处理类似的情况并用这两个类解决了它,我对此并不完全满意,但也许它有帮助..

public class RootProperty<T>
{
    private T _value;

    public virtual T Value
    {
        get { return _value; }
        set
        {
            if (Equals(value, _value)) return;
            _value = value;
        }
    }

    public static implicit operator T(RootProperty<T> p)
    {
        return p.Value;
    }

    public override string ToString()
    {
        return "[RootProperty<" + typeof(T).Name + ">] " + Value;
    }
}

public class InheritedProperty<T> : RootProperty<T>
{
    private bool _override;

    public bool Override
    {
        get { return _override; }
        set
        {
            if (value.Equals(_override)) return;
            _override = value;

            //If we now override and we had no value before, copy the value that was previously inherited for convinience
            if (_override && (Value == null || Value.Equals(default(T))))
                Value = Parent.Value;

        }
    }

    public RootProperty<T> Parent { get; private set; }

    public override T Value
    {
        get
        {
            if (Override)
            {
                return base.Value;
            }

            if (Parent == null)
                throw new Exception("Parent musn't be null");
            return Parent.Value;
        }
        set
        {
            Override = true;
            base.Value = value;
        }
    }

    public InheritedProperty(RootProperty<T> parent)
    {
        Parent = parent;
    }

    public override string ToString()
    {
        return "[InheritedProperty<" + typeof(T).Name + ">] " + Value;
    }
}

示例用法

class TestParent
{
    public RootProperty<int> MyInt { get; private set; }

    public TestParent()
    {
        MyInt = new RootProperty<int>();
    }

}

class TestChild
{
    public InheritedProperty<int> MyInt { get; private set; }

    public TestChild(TestParent parent)
    {
        MyInt = new InheritedProperty<int>(parent.MyInt);
    }
}
于 2013-04-29T13:33:23.960 回答
1

我建议使用Modified Preorder Tree Traversal来组织类,然后将它们存储在一个List<MyClass>. 然后,您可以使用 LINQ 进行排序并找到该属性的第一个非空值。我不能说它的性能,但它可能值得一试。

LINQ 应该类似于:

var node = myList.Where(x => x.LeftNumber <= target.LeftNumber && x.RightNumber >= target.RightNumber)
                 .OrderByDescending(x => x.LeftNumber)
                 .FirstOrDefault(x => x.Prop != double.NaN);
return (node != null) ? node.Prop : double.NaN;

x.LeftNumber <= target.LeftNumber && x.RightNumber >= target.RightNumber将获得target节点、它的父节点和所有其他祖先。

将对它们进行排序,OrderByDescending因此树的底部是第一个。您可以选择使用Last()而不是OrderBy().First()

FirstOrDefault()将获得第一个实际具有值的值,从target节点开始并向上处理树。


编辑: 这是我用来从父子关系重建树的方法。

protected static void RebuildTree()
{
    RebuildTree(allNodes[0], 0);
    SaveAllNodes();
}
private static int RebuildTree(Taxonomy node, int left)
{
    node.leftNumber = left;
    node.rightNumber = left + 1;
    foreach (Taxonomy child in node.Children)
    {
        node.rightNumber = RebuildTree(child, node.rightNumber);
    }
    return node.rightNumber + 1;
}
于 2013-04-29T16:08:03.943 回答