使用默认的引用对象关系来访问c#中属性的默认值
本文关键字:访问 属性 默认值 关系 默认 引用 对象 | 更新日期: 2023-09-27 18:11:15
我有一个层次结构中的对象,它的值可以默认为父对象的值。两个对象的类型相同。
作为一个例子:如果Score
属性是double.NaN
,那么Score
值应该从Parent
属性指向的对象中检索,但只有在设置了Parent
(Parent != null
)的情况下。
我的问题是,我怎样才能可靠地、通用地实现这样一个模型?我有两个选择,也许还有更多?
选项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都需要手动实现,这很容易出错
- 性能受到每次get和set 的影响
选项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 ?)。
您可以使用wpf风格的方法,其中属性值不存储在字段中,而是存储在基类的字典中。
这样所有的属性看起来都像
double Prop {
get{ return (double)GetValue("Prop"); }
set{ SetValue("Prop",value); }
}
GetValue将检查所有父类,如果当前类没有设置默认值,则查找默认值。
我正在处理一个类似的场景,并解决了这两个类,我不是完全满意它,但也许它有帮助…
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);
}
}
我建议使用修改的预购树遍历来组织类,然后将它们存储在单个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;
}