如何使子对象知道父对象上分配给它的属性名称?
本文关键字:对象 属性 何使 分配 | 更新日期: 2023-09-27 18:03:46
UPDATE:既然好奇的人想知道,这里有一些关于为什么我需要做我正在做的事情的背景:
我的c#应用程序(应用程序A)与另一个应用程序(应用程序B,这不是我的)接口,它发送给我一个Parent Id,一个在Parent对象图中导航的路径,以及一个到达那里后要设置的值。应用程序B不知道c#应用程序中Parent模型中嵌套对象的id,它不知道Parent有多个实例;它的工作只是接受来自外部设备的低级请求,并将它们转换为稍微高级的东西,然后将它们交给应用程序a。父对象中的每个子对象都有一个唯一的路径-没有两个引用会指向相同的子对象。因此,B可能会向A发送如下JSON对象:
{
'Id' : 12345,
'Path' : 'ChildProperty.NestedChildProperty.AnotherNestedChildProperty',
'Value': 'Ligers are bred for their skills in magic.'
}
还有其他选项可以解决此问题,例如从叶子子对象向上导航或从父对象向下导航,但就数据库查询和应用程序处理而言,这些都远不如通过父对象所属的Id及其在父对象图中的路径来查询子存储库的效率高。
下面的例子是裸机。它甚至没有说明嵌套的子对象。在没有提供到目前为止的上下文的情况下,尽可能小地说明问题中提出的问题。
所以…
假设我有两个这样的类:
public class Parent
{
public Child ChildProperty {get; set;}
}
public class Child
{
public string ParentPropertyName {get; protected set;}
}
值得注意的是,在此模型中,子节点永远不会被多个父节点引用。我想做的是让Child
实例意识到它被分配给的属性名称,所以使用这个调用:
parent.ChildProperty = new Child();
我parent.ChildProperty.ParentPropertyName == "ChildProperty" //evaluates to true
我已经下了一些奇怪的兔子洞试图使用事件和表达式/MemberExpressions和工厂和反射和递归图遍历的一些组合,试图使这项工作,但没有什么,只是为事实,分配到父属性是在子实例化后完成。在子节点上手动执行如下操作:
parent.ChildProperty = new Child("ChildProperty");
不能作为选项,因为它太容易出错。
我能想到的最好的是一种中间立场,看起来或多或少是这样的:
public class Parent
{
private Child childProperty;
public Child ChildProperty
{
get { return childProperty; }
set
{
childProperty = value;
if (childProperty.ParentPropertyName == null)
{
childProperty.SetParentPropertyName(() => this.ChildProperty);
}
}
}
}
public class Child
{
public string ParentPropertyName { get; protected set; }
internal void SetParentPropertyName(Expression<Func<object>> exp)
{
this.ParentPropertyName = (((MemberExpression) (exp.Body)).Member).Name;
}
}
虽然这可以工作并且比手动传递字符串要好得多,但实际的类更复杂,使用更宽的&更深入的对象图,因此我必须在需要的每个类的每个属性上复制setter逻辑。最重要的是,它将真正属于Child
的功能转移到Parent
。
所以这让我回到了我的问题-是否有一种方法可以使Child
实例意识到Parent
上的属性名称,而不需要Parent
实例的干预?
我认为你不能可靠地实现它。不是因为技术原因,而是因为逻辑原因。
假设你有:
public class Parent
{
public Child ChildProperty {get; set;}
public Child ChildProperty2 {get; set;}
}
public class Child
{
public string ParentPropertyName {get; protected set;}
}
var child = new Child();
parent.ChildProperty = child;
parent.ChildProperty2 = child;
现在期望child.ParentPropertyName
的名称是什么?ChildProperty
还是ChildProperty2
?
问题本身没有意义。如果你能解释你的意图,这个问题就很容易回答了。
对于额外的上下文,我将提供一个完全独立的答案,以完全不同的方式解决它。
你得到的似乎是一棵被压扁的树。也就是说,每个节点都知道自己的路径,而不仅仅是它的父节点。当你用它制作对象时,你可以把它转换回树。这是一个Data
类,它代替了Parent
和Child
。
class Data
{
public int ID { get; set; }
public string Value { get; set; }
public Dictionary<string, Data> Children { get; set; }
public Data()
{
Children = new Dictionary<string, Data>();
}
public Data(int Id, string value) : this()
{
this.ID = Id;
this.Value = value;
}
public Data this[string name]
{
get {
if (Children.ContainsKey(name)) return Children[name];
else {
Children.Add(name, new Data());
return Children[name];
}
}
set {
if (Children.ContainsKey(name)) Children[name] = value;
else Children.Add(name, value);
}
}
}
用法:
var data = new Data();
data["ChildProperty"]["NestedChildProperty"]["AnotherNestedChildProperty"] =
new Data(12345, "Ligers are bred for their skills in magic.");
Console.WriteLine(data["ChildProperty"]["NestedChildProperty"]["AnotherNestedChildProperty"].Value);
如何将Path
解析为它的离散部分取决于您。
请注意,这个版本没有提供从子节点到父节点的机制,但是很容易添加一个public Data(Data parent)
构造函数而不是空的构造函数,并添加一个属性来存储它。
我仍然想知道你为什么要这样做,但反射是你的答案。有两个步骤:
首先,Child
必须知道Parent
要看什么。这可以通过以下两种方式之一来实现。其次,Child
必须知道如何从Parent
获得名称-这非常直接。
首先处理第二部分,将这个函数添加到Child
对象:
private static string GetParentPropertyName(Parent parent, Child child)
{
var matches = parent.GetType().GetProperties()
.Where(x => x.GetValue(parent, null) == child);
return matches.Single().Name;
}
这将检查指定Parent
上的所有属性,以找到存储指定Child
的属性。然后它将返回该属性的Name
。请注意,如前所述,如果没有恰好有一个属性持有Child
,则会抛出异常。根据您的场景进行适当的调整。
对于第一部分,有两种方法可以调用该函数。哪一个最好取决于你的用法。
如果您要保证1:1的关系,那么最好的方法是在创建
Parent
时将其传递给Child
。你的Child
对象看起来像这样:public class Child { private Parent _parent; public Child(Parent parent) { _parent = parent; } public string ParentPropertyName { get { return GetParentPropertyName(_parent, this); } } }
我建议在首次访问
Parent.Child
或首次创建Parent
时自动创建Child
。如果您不想保证这种关系,请将以下函数添加到
Child
中,而不是拥有ParentPropertyName
属性:public string GetParentPropertyName(Parent parent) { return GetParentPropertyName(parent, this); }
你可以在
Parent
中添加一个函数或属性来执行return this.Child.GetParentPropertyName(this);
我真的不明白这个
parent.ChildProperty.ParentPropertyName == "ChildProperty"
也许你只是需要
public class Parent
{
public Child Child {get; set;}
}
public class Child
{
public Parent Parent {get; set;}
}
这样他们就能找到彼此。不管出于什么原因,添加text属性,这样就可以执行
parent.Child.WhateverProperty == "WhateverProperty"
所以这让我回到了我的问题-有没有一种方法可以使子实例意识到父实例上的属性名称,而不需要父实例的干预?
他们一定互相了解(所以我的猜测多少是对的)。然后:
public class Parent
{
public Child Child {get; set;}
public string WhateverProperty {get; set;}
}
public class Child
{
public Parent Parent {get; set;}
public string WhateverProperty
{
get { return Parent.WhateverProperty; }
set { Parent.WhateverProperty = value; }
}
}
如果缺少父元素,则可以返回"Something"