我对利用 lambda 表达式创建属性选择器树感兴趣。
使用场景是,我们有一些代码对对象图进行一些递归反射,为了限制递归的范围,我们目前使用 Attributes 来标记应该遍历哪些属性。即获取对象的所有修饰属性,如果该属性是具有修饰属性的引用类型,则也对每个属性重复。
使用属性的限制是您只能将它们放置在您控制源的类型上。 lambda 表达式树允许在任意类型的公共成员上定义范围。
如果有一种简写方式来定义这些表达式,它会反映对象图的结构,这会很方便。
最终,我希望有这样的东西:
Selector<MyType> selector = new [] {
(t => Property1),
(t => Property2)
{
p => NestedProperty1,
p => NestedProperty2
}
};
现在,我能做的最好的事情就是为每个节点明确声明一个实例,如下所示:
var selector = new Selector<MyType>()
{
new SelectorNode<MyType, Property1Type>(t => Property1),
new SelectorNode<MyType, Property2Type>(t => Property2)
{
new SelectorNode<Property2Type, NestedProperty1Type>(p => NestedProperty1),
new SelectorNode<Property2Type, NestedProperty2Type>(p => NestedProperty2)
},
};
这段代码没有任何问题,但是您必须显式写出每个节点的类型参数,因为编译器无法推断类型参数。这是一种痛苦。而且丑陋。我已经看到了一些令人难以置信的语法糖,并且确信一定有更好的方法。
由于我对“更高”的 C# 概念(例如动力学、协/逆变泛型和表达式树)缺乏理解,我想我应该把这个问题提出来,看看是否有任何专家知道实现这一目标的方法(或者类似的东西)它?)
作为参考,这些是声明Selector
and SelectorNode
实现我在帖子中描述的结构的类:
public interface ISelectorNode<T> {}
public class Selector<T>: List<ISelectorNode<T>>{}
public class SelectorNode<T, TOut>: List<ISelectorNode<TOut>>, ISelectorNode<T>
{
public SelectorNode(Expression<Func<T, TOut>> select) {}
}
//Examples of Usage below
public class Dummy
{
public ChildDummy Child { get; set; }
}
public class ChildDummy
{
public string FakeProperty { get; set; }
}
public class Usage
{
public Usage()
{
var selector = new Selector<Dummy>
{
new SelectorNode<Dummy, ChildDummy>(m => m.Child)
{
new SelectorNode<ChildDummy, string>(m => m.FakeProperty)
}
};
}
}
为了扩展 nawal 的答案而进行了编辑:
利用 C# 的集合初始值设定项语法,我们可以获得如下代码:
var selector = new Selector<Dummy>
{
(m => m.Child),
{dummy => dummy.Child,
c => c.FakeProperty,
c => c.FakeProperty
}
};
如果我们的 SelectorNode 类的 Add 方法如下所示:
public class Selector<T> : List<ISelectorNode<T>>
{
public SelectorNode<T, T, TOut> Add<TOut>(Expression<Func<T, TOut>> selector, params Expression<Func<TOut, object>>[] children)
{
return SelectorNode<T, T, TOut>.Add(this, this, selector);
}
}
必须有一种方法来利用这种语法!