如何让子对象知道在其父对象上分配的属性名称?

时间:2014-05-15 15:15:01

标签: c#

更新:,因为询问的大脑想要知道,这里有一些关于为什么我需要做我正在做的事情的背景:

我的C#应用​​程序(应用程序A)与另一个应用程序(应用程序B,不是我的应用程序)接口,该应用程序向我发送父ID,导航到父对象图中的路径,以及一旦到达那里就设置的值。 App B没有C#app中Parent模型中嵌套对象的ID的概念,它没有概念,即父项的实例不止一个;它的工作就是接受来自外部设备的低级请求,并将它们转换为更高级别的东西并将它们交给app A. Parent中的每个Child对象都有一个唯一的路径 - 没有两个引用指向同一个Child对象。所以B可能会发送一个类似于以下JSON对象的东西:

{
    'Id'   : 12345,
    'Path' : 'ChildProperty.NestedChildProperty.AnotherNestedChildProperty',
    'Value': 'Ligers are bred for their skills in magic.'
}

还有其他选项可以解决此问题,例如从叶子对象导航或从父对象向下导航,但这些选项在数据库查询和应用程序处理方面都远远低于查询能力子女的存储库由他们所属的父母的Id和他们在父对象图中的路径。

以下示例是准系统。它甚至没有说明嵌套的Child对象。在没有提供到目前为止的上下文的情况下,尽可能小地说明问题中提出的问题。

那就是......


假设我有几个类似的话:

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

我一直在试图使用某些组合的事件和表达式/成员表达式和工厂以及反射和递归图遍历来尝试使这项工作,但没有任何事情,只是为了分配到在实例化Child之后完成父属性。像这样手动对孩子这样做:

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逻辑。最重要的是,它将功能转移到真正属于Parent的{​​{1}}。

这让我回到我的问题 - 有没有办法让Child实例知道Child上的属性名称,而没有来自{{1}的干预实例?

4 个答案:

答案 0 :(得分:2)

我认为你无法可靠地实现它。不是因为技术原因,而是因为合乎逻辑。

假设你有:

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名称是什么? ChildPropertyChildProperty2

作为立场的问题没有意义。如果你可以解释什么是意图,那么很容易回答。

答案 1 :(得分:1)

有了额外的上下文,我将提供一个完全独立的答案,以完全不同的方式解决它。

你给的东西似乎是一棵扁平的树。也就是说,每个节点都知道自己的路径,而不仅仅是它的父节点。当您从中生成对象时,可以将其转换回树。这是一个Data类,而不是ParentChild

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解析为它的离散部分。

请注意,此版本不提供从子级到其Parent的机制,但是很容易添加public Data(Data parent)构造函数而不是空构造函数,以及存储它的属性。 / p>

答案 2 :(得分:0)

我真的不明白这个

parent.ChildProperty.ParentPropertyName == "ChildProperty"

也许你只需要

public class Parent
{
    public Child Child {get; set;}
}

public class Child 
{
    public Parent Parent {get; set;}
}

这样他们就能找到对方。无论出于何种原因添加该文本属性,因此您可以执行

parent.Child.WhateverProperty == "WhateverProperty"

  

这让我回到我的问题 - 是否有一种方法可以让Child实例知道在没有父实例干预的情况下分配给它的Parent上的属性名称?

他们必须彼此了解(所以我的猜测有点正确)。然后:

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"

答案 3 :(得分:0)

我仍然想知道你为什么要这样做,但是反思是你的答案。这有两个步骤:

首先,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:1的关系,那么最好的方法是在Parent创建时将Child传递给Child。您的public class Child { private Parent _parent; public Child(Parent parent) { _parent = parent; } public string ParentPropertyName { get { return GetParentPropertyName(_parent, this); } } } 对象将如下所示:

    Child

    我建议在首次访问Parent.Child或首次创建Parent时自动创建Child

  2. 如果您不想保证这种关系,请将以下功能添加到ParentPropertyName,而不是public string GetParentPropertyName(Parent parent) { return GetParentPropertyName(parent, this); } 属性:

    Parent

    然后,您可以向return this.Child.GetParentPropertyName(this); 添加功能或属性

    {{1}}