使用反射获取到特定键的字典完整路径

本文关键字:字典 路径 反射 获取 | 更新日期: 2023-09-27 18:03:51

我有一个嵌套的IDictionary,像这样;

class SomeObject {
    public IDictionary<string, IDictionary<string, decimal>> Entries { get; set; }
}

一个对象可能是这样的;

var o = new SomeObject {
   Entries = new ... {
      ["first"] = new ... {
         ["a"] = 1.0,
         ["b"] = 2.0
      },
      ["second"] = new ... {
      }
   }
};

我试图找出如何获得"路径"到一个嵌套的键作为string s使用lambda表达式的集合。例如:

class SomeFactory {
    public SomeFactory Use<T>(T entity) {
        this.Model = entity; return this;
    }
    public IEnumerable<string> Get<TProperty>(Expression<Func<T, IEnumerable<TProperty>>> selector) {
        var properties = ... // get list of properties from [selector] //
    }
}
var factory = new SomeFactory();
var list = factory.Use<SomeObject>(o).Get(n => n.Entries["first"]["b"]);

其中结果将是具有值的IEnumerable<string>{"Entries","first","b"} .

这是可能的吗?

用例

我想这样做的原因是因为我正在使用一个库,它可以使用一个对象以某种方式发出命令,看起来像这样(伪代码);

class Patch {
   string Property;
   object Value;
   Patch[] Nested;
} 

每个Patch都可以取从给定点反序列化的对象上的1属性名。它将对它执行一个非常快的操作,比从数据库加载整个对象、更新它并再次保存它要快得多。这在程序的各个部分都很重要,原因有很多。(这不是一个SQL数据库)

如果给出了nested补丁,它将不设置顶级属性的值,而是查找顶级属性并将其用作执行数组中下一个Patch的启动点。这个过程一直持续到最后一个,然后进行更改。

因此,为了向IDictionary发布补丁,整个对象图需要看起来类似于…

{
    "Property": "Entries",
    "Nested": [
        {
            "Property": "first",
            "Nested": [
                {
                    "Property": "b",
                    "Value": 7.0
                }
            ]
        }
    ]
}

这不是问题,但每次都是一个很麻烦的图表。我的想法是使整个过程更简单,能够从一个lambda表达式构建这个图,在IDictionary对象上找到所需的目的地;即f(n => n["first"]["b"])

使用反射获取到特定键的字典完整路径

下面的代码适合上面的场景:

public static IEnumerable<string> Get<T>(Expression<Func<T, object>> selector)
{
    var list = new List<string>();
    Expression exp = (selector.Body as UnaryExpression).Operand as MethodCallExpression;
    while (exp is MethodCallExpression)
    {
        var call = exp as MethodCallExpression;
        var arg = call.Arguments[0].ToString();
        if(call.Arguments[0].Type == typeof(string))
        {
            arg = arg.Substring(1, arg.Length - 2);
        }
        list.Add(arg);
        exp = call.Object as Expression;
    }
    var member = exp as MemberExpression;
    list.Add(member.Member.Name);
    list.Reverse();
    return list;
}
static void Main(string[] args)
{
    var graph = Get<SomeObject>(o => o.Entries["first"]["b"]);
    foreach (var node in graph)
    {
        Console.WriteLine(node);
    }            
    Console.ReadLine();
}

输出是:

条目首先

b