I am interested in utilizing lambda expressions to create a tree of property selectors.
The usage scenario is that we have some code that does some recursive reflection on an object graph, and to limit the scope of recursion, we're currently using Attributes to mark which properties should be traversed. i.e. Get all decorated properties of object, if that property is a reference type with decorated properties, repeat for each of those too.
The limitation of using Attributes is that you can only place them on Types you control the source for. A tree of lambda expressions allows the scope to be defined on any arbitrary type's public members.
It would be handy to have a shorthand way to define these expressions, which reflects the structure of the object graph.
Ultimately, I'd love to have something like this:
Selector<MyType> selector = new [] {
(t => Property1),
(t => Property2)
{
p => NestedProperty1,
p => NestedProperty2
}
};
Right now, the best I can do declares an instance for each node explicitly something like this:
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)
},
};
There's nothing wrong with this code, but you have to write out the type arguments for each node explicitly, since the compiler can't infer the type arguments. This is a pain. And ugly. I've seen some incredible syntactical sugar out there, and am sure there must be a better way.
Owing to my lack of understanding of 'higher' C# concepts like dynamics, co/contravariant generics and expression trees, I thought I'd pitch the question out there and see if any gurus know of a way to achieve this (or something rather like it?)
For reference, these are the declarations for the Selector
and SelectorNode
classes that achieve the structure I described in my post:
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)
}
};
}
}
Edited in the interest of expanding on nawal's answer:
Leveraging C#'s collection initializer syntax, we can get code to look like:
var selector = new Selector<Dummy>
{
(m => m.Child),
{dummy => dummy.Child,
c => c.FakeProperty,
c => c.FakeProperty
}
};
This is if our SelectorNode class' Add method looks like:
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);
}
}
There must be a way to leverage this syntax!