C# 按路径访问嵌套结构(即“Model.NestedModel.ListOfThings[1]”)

C# 按路径访问嵌套结构(即“Model.NestedModel.ListOfThings[1]”),c#,c#-4.0,nested,C#,C# 4.0,Nested,鉴于以下类别和数据: public class InnerExample { public string Inner1 { get; set; } } public class Example { public string Property1 { get; set; } public string Property2 { get; set; } public List<InnerExample> Inner { get; set; } } va

鉴于以下类别和数据:

public class InnerExample
{
    public string Inner1 { get; set; }
}


public class Example
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
    public List<InnerExample> Inner { get; set; }
}

var a = new Example
{
    Property1 = "Foo",
    Property2 = "Bar",
    Inner = new List<InnerExample>
    {
      new InnerExample
      {
        Inner1 = "This is the value to change"
      }
   }
};
在这个特殊的例子中,我知道我永远不会访问一个不存在的密钥,所以我不太关心错误检查


(如果以前有人问过这个问题,很抱歉。我做了一些搜索,但很快就没有关键字了。)

没有内置的功能,但也可以这样做(尽管它不会很琐碎)

您要做的是向类添加一个
示例
。在索引器中,您必须将提供的“属性路径”解析为多个步骤,并使用这些步骤逐步解析目标属性

例如,在将
internal[0].Inner1
解析为三个不同的步骤(获取
internal
,然后从该获取
[0]
,然后从该
Inner1
)之后,您将有一个类似以下内容的循环:

// This works only with plain (non-indexed) properties, no error checking, etc.
object target = this;
PropertyInfo pi = null;
foreach (var step in steps)
{
    pi = target.GetType().GetProperty(step);
    target = pi.GetValue(target);
}

// And now you can either return target (on a get) or use pi.SetValue (on a set)

乔恩,多亏了你给我的基本建议,我想出了一个适合我的案例的解决方案

  • 没有错误检查
  • 必须设置属性,而不是数组元素
  • 我相信有更有效的方法来做到这一点。。。我远非反思方面的专家

    /// <summary>
    /// Take an extended key and walk through an object to update it.
    /// </summary>
    /// <param name="o">The object to update</param>
    /// <param name="key">The key in the form of "NestedThing.List[2].key"</param>
    /// <param name="value">The value to update to</param>
    private static void UpdateModel(object o, string key, object value)
    {
        // TODO:
        // Make the code more efficient.
    
        var target = o;
        PropertyInfo pi = null;
    
        // Split the key into bits.
        var steps = key.Split('.').ToList();
    
        // Don't walk all the way to the end
        // Save that for the last step.
        var lastStep = steps[steps.Count-1];
        steps.RemoveAt(steps.Count-1);
    
        // Step through the bits.
        foreach (var bit in steps)
        {
            var step = bit;
    
            string index = null;
    
            // Is this an indexed property?
            if (step.EndsWith("]"))
            {
                // Extract out the value of the index
                var end = step.IndexOf("[", System.StringComparison.Ordinal);
                index = step.Substring(end+1, step.Length - end - 2);
    
                // and trim 'step' back down to exclude it.  (List[5] becomes List)
                step = step.Substring(0, end);
            }
    
            // Get the new target.
            pi = target.GetType().GetProperty(step);
            target = pi.GetValue(target);
    
            // If the target had an index, find it now.
            if (index != null)
            {
                var idx = Convert.ToInt16(index);
    
                // The most generic way to handle it.
                var list = (IEnumerable) target;
                foreach (var e in list)
                {
                    if (idx ==0)
                    {
                        target = e;
                        break;
                    }
                    idx--;
                }
            }
        }
    
        // Now at the end we can apply the last step,
        // actually setting the new value.
        if (pi != null || steps.Count == 0)
        {
            pi = target.GetType().GetProperty(lastStep);
            pi.SetValue(target, value);
        }
    }
    
    //
    ///获取扩展关键点并遍历对象以更新它。
    /// 
    ///要更新的对象
    ///“NestedThing.List[2].key”形式的键
    ///要更新到的值
    私有静态void UpdateModel(对象o、字符串键、对象值)
    {
    //待办事项:
    //使代码更高效。
    var目标=o;
    PropertyInfo pi=null;
    //把钥匙分成几位。
    var steps=key.Split('.').ToList();
    //不要走到尽头
    //把它留到最后一步。
    var lastStep=steps[steps.Count-1];
    步骤。移除(步骤。计数-1);
    //逐字逐句。
    foreach(步进变量位)
    {
    var步长=位;
    字符串索引=null;
    //这是索引属性吗?
    if(步骤结束(“]”)
    {
    //提取出索引的值
    var end=step.IndexOf(“[”,System.StringComparison.Ordinal);
    索引=步长子串(结束+1,步长-结束-2);
    //并向下修剪“台阶”以排除它。(列表[5]变为列表)
    步骤=步骤子字符串(0,结束);
    }
    //找到新的目标。
    pi=target.GetType().GetProperty(步骤);
    目标=pi.GetValue(目标);
    //如果目标有索引,请立即查找。
    如果(索引!=null)
    {
    var idx=转换为16(索引);
    //最通用的处理方法。
    变量列表=(IEnumerable)目标;
    foreach(列表中的变量e)
    {
    如果(idx==0)
    {
    目标=e;
    打破
    }
    idx-;
    }
    }
    }
    //现在我们可以应用最后一步,
    //实际设置新值。
    if(pi!=null | |步数.计数==0)
    {
    pi=target.GetType().GetProperty(最后一步);
    pi.设定值(目标、值);
    }
    }
    

谢谢。这让我找到了自己的解决方案,我已经在这里发布了。
/// <summary>
/// Take an extended key and walk through an object to update it.
/// </summary>
/// <param name="o">The object to update</param>
/// <param name="key">The key in the form of "NestedThing.List[2].key"</param>
/// <param name="value">The value to update to</param>
private static void UpdateModel(object o, string key, object value)
{
    // TODO:
    // Make the code more efficient.

    var target = o;
    PropertyInfo pi = null;

    // Split the key into bits.
    var steps = key.Split('.').ToList();

    // Don't walk all the way to the end
    // Save that for the last step.
    var lastStep = steps[steps.Count-1];
    steps.RemoveAt(steps.Count-1);

    // Step through the bits.
    foreach (var bit in steps)
    {
        var step = bit;

        string index = null;

        // Is this an indexed property?
        if (step.EndsWith("]"))
        {
            // Extract out the value of the index
            var end = step.IndexOf("[", System.StringComparison.Ordinal);
            index = step.Substring(end+1, step.Length - end - 2);

            // and trim 'step' back down to exclude it.  (List[5] becomes List)
            step = step.Substring(0, end);
        }

        // Get the new target.
        pi = target.GetType().GetProperty(step);
        target = pi.GetValue(target);

        // If the target had an index, find it now.
        if (index != null)
        {
            var idx = Convert.ToInt16(index);

            // The most generic way to handle it.
            var list = (IEnumerable) target;
            foreach (var e in list)
            {
                if (idx ==0)
                {
                    target = e;
                    break;
                }
                idx--;
            }
        }
    }

    // Now at the end we can apply the last step,
    // actually setting the new value.
    if (pi != null || steps.Count == 0)
    {
        pi = target.GetType().GetProperty(lastStep);
        pi.SetValue(target, value);
    }
}