This piece of code is part of a personal bigger project that I am working on and I think there are interesting thing in it and I wanted to share it with you.

I am trying to use generics and type inference to imagine new kind of method prototype.
With all the stuff around Linq like lambda expressions, simple method calls have become extremely powerful.

What I have been trying here is to create a generic way to define a selection starting from a tree of objects, the most simpler way as possible.

Let's start with the beginning...
Here is a very simple little interface defining a node of the tree, basically, a value and the references to the child nodes.

 

public interface INodeSelector { object Value { get; } List<INodeSelector> Children { get; } }

You can notice that the Value is just typed as 'object'. To store any kind of value we will keep this definition, but to have an easy to use initialization, we will create generic methods and have all the benefits of the type inference and intellisense.

Now I will create an generic type to implement INodeSelector: NodeSelector<T>.

new NodeSelector<Customer>(customer);

As constructors can not infer their generic parameters from the parameters, I prefered to use a static method for creating NodeSelector<T> instances. For different reasons that I will explain later the name of the hosting class is not NodeSelector but TreeSelector.

public static class TreeSelector { public static NodeSelector<T> Create<T>(T source) { return new NodeSelector<T>(source); } } ... var ts = TreeSelector.Create(customer)

For the moment we just have NodeSelector<T> class that keeps the root value of our tree.
Now from this root, I would like to define paths to some other values. Each of those paths will provide a new child node attached to the root node (and to its parent node for the following levels).

Let's start thinking about just a single child.

We can achieve this easily using a simple selector expression, starting from the value of the current node and returning the child value.

public class NodeSelector<T> : INodeSelector { ... public void Add<TResult>(Func<T, TResult> selector) { Children.Add(TreeSelector.Create(selector(source))); } }

Then we can write:

var ts = TreeSelector.Create(customer); ts.Children.Add(c => c.CustomerID);

Now I would like to add two features:

adding new nodes at the same level.

  • adding new child nodes to an existing node.

Let's remind some generic rules. When calling 'ts.Children.Add(c => c.CustomerID)', we do not need to define the TResult generic parameter of Add<TResult> because it is infered by the expression 'c => c.CustomerID'. Without type inference we would have need to define Add<string>() explicitly.

I could return the new created node in the Add() method instead of returning void and then chain Add() calls:

ts.Children.Add(c => c.Address).Children.Add(ad => ad.City);

But if I want to add nodes at the same level (sibling), I will have to create a temporary reference and break this chain.

On the other hand, I could allow to pass an array of selectors to the Add method to add sibling nodes in just a call.

ts.Children.Add(c => c.CustomerID, c => c.Address);

Using this syntax, I can add as many sibling nodes as I want but returning a value from Add() has no sense so I am losing the previous syntax for chaining children creation.

So I have imagined something else that is not very usual: returning the current node instance from the Add() method (not the added one !).

public class NodeSelector<T> : INodeSelector { ... public NodeSelector<T> Add<TResult>(Func<T, TResult> selector) { Children.Add(TreeSelector.Create(selector(source))); return this; } }

Now we can write:

var ts = TreeSelector.Create(customer) .Add(c => c.CustomerID) .Add(c => c.Address);

So we now use chained Add() calls to add sibling nodes. We still need to add child nodes.
There is something very interesting in type inference: if a generic parameter is resolved by the first parameter of a method, then the following parameters can use it.

In my example, '.Add(c => c.Address, ...)', the TResult parameter is resolved as an Address type thanks to the first parameter 'c => c.Address'. So we can use it in the following parameters.

Inside the Add() method we are wrapping the resulting value of the projection in a new NodeSelector<TResult>. We would need to get it out so the second parameter could use it to add child nodes. To achieve this I will add a new delegate parameter providing the added NodeSelector<T> and waiting for it to be returned. In theory we don't need any return value but lambda expressions must return a value. As now, all Add() calls are returning the NodeSelector<T> itself, I will define my delegate that way:

public NodeSelector<T> Add<TResult>(Func<T, TResult> selector, Func<NodeSelector<TResult>, NodeSelector<TResult>> childrenSelectors) { var n = TreeSelector.Create(selector(source)); childrenSelectors(n); Children.Add(n); return this; }

Now we can write:

 

var ts = TreeSelector.Create(customer) .Add(c => c.CustomerID) .Add(c => c.Address, ad => ad .Add(a => a.City) .Add(a => a.Country) );

Now it becomes quite interesting. We have a single expression allowing us to add both siblings and child nodes without creating any temporary reference to any node.

Playing with indentation we can even 'see' the resulting tree.

Let's try to display it in a TreeView control.
The code is recursive but quite simple:

private void FillTreeView(TreeNodeCollection nodes, INodeSelector ns) { var node = nodes.Add(ns.Value.ToString()); foreach (var child in ns.Children) FillTreeView(node.Nodes, child); } ... FillTreeView(treeView1.Nodes, ts);

image 

Now we have a good basic solution, let's add some more features !

In the current example, we have built a tree using simple selectors, jumping to one value to another one. (one-to-one relationship)

What if the relationship is one-to-many like 'c => c.Orders' ?

Then we could create a method to add all the children into as many sibling nodes.
So we will need a selector to retrieve the collection (c => c.Orders). Then another selector to apply on each item of the collection. It the itemSelector is not provided (null) we will add the item itself (the Order in our example).

public NodeSelector<T> AddRange<TResult>( Func<T, IEnumerable<TResult>> collectionSelector, Func<TResult, object> itemSelector) { foreach (var item in collectionSelector(source)) if (itemSelector != null) Children.Add(TreeSelector.Create(itemSelector(item))); else Children.Add(TreeSelector.Create(item)); return this; } public NodeSelector<T> AddRange<TResult>( Func<T, IEnumerable<TResult>> collectionSelector) { return AddRange(collectionSelector, null); }

Now we can write :

var ts = TreeSelector.Create(customer) .Add("CustomerID", c => c.CustomerID) .Add(c => c.Address, ad => ad .Add(a => a.City) .Add(a => a.Country) ) .AddRange(c => c.Orders, o => o.OrderDate);

image

We can see that the AddRange() method has added 3 new nodes. So the AddRange() is just the repeat of what we can do with Add().
Maybe would like to group all these nodes under a common one whose value would be the collection.

Let's create the AddCollection() method.

public NodeSelector<T> AddCollection<TResult>( Func<T, IEnumerable<TResult>> collectionSelector, Func<TResult, object> itemSelector) { var selector = collectionSelector(source); var node = TreeSelector.Create(selector); Children.Add(node); foreach (var item in selector) if (itemSelector != null) node.Children.Add(TreeSelector.Create(itemSelector(item))); else node.Children.Add(TreeSelector.Create(item)); return this; } public NodeSelector<T> AddCollection<TResult>( Func<T, IEnumerable<TResult>> collectionSelector) { return AddCollection(collectionSelector, null); }

Now we can write:

var ts = TreeSelector.Create(customer) .Add(c => c.CustomerID) .Add(c => c.Address, ad => ad .Add(a => a.City) .Add(a => a.Country)) .AddCollection(c => c.Orders, o => o.OrderDate);

image

Ok, one last feature, but a useful one.

As you can see in all the screen captures, I am just displaying the Values because it's the only thing I have !! :-)

But ! Maybe you would like to add a label or any kind of other object to your nodes.

In the Winforms TreeNode for example, there is a very useful Tag property that allows you to attach what you want to your nodes.

Let's add such a feature to our code.

Let's start with the NodeSelector definition itself:

an extended interface :

public interface INodeSelectorWithTag : INodeSelector { object Tag { get; } }

an extended implementation :

public class NodeSelector<T, TTag> : NodeSelector<T>, INodeSelector<T, TTag> { public NodeSelector(TTag tag, T source) : base(source) { Tag = tag; } public TTag Tag { get; private set; } object INodeSelectorWithTag.Tag { get { return this.Tag; } } public NodeSelector<T, TTag> Add<TResult>( TTag tag, Func<T, TResult> selector) { Children.Add(TreeSelector<TTag>.Create(tag, selector(source))); return this; } and all the AddXXX() methods ...

and an extended creator class :

public static class TreeSelector<TTag> { public static NodeSelector<T, TTag> Create<T>(TTag tag, T source) { return new NodeSelector<T, TTag>(tag, source); } public static NodeSelector<T, TTag> Create<T>(T source) { return TreeSelector<TTag>.Create(default(TTag), source); } }

Now we can write :

var ts = TreeSelector<string>.Create("Customer", customer) .Add("CustomerID", c => c.CustomerID) .Add("Address", c => c.Address, ad => ad .Add("City", a => a.City) .Add("Country", a => a.Country) );

Let's modify our TreeView code.

 

private void FillTreeView(TreeNodeCollection nodes, INodeSelectorWithTag ns) { var node = nodes.Add((ns.Tag ?? "") + " > " + ns.Value.ToString()); foreach (var child in ns.Children) FillTreeView(node.Nodes, child); }

image

This post is already too long so I will stop here.

What's next ?

- I will use this TreeSelector to project an Xml structure.

- I will rewrite it to support deferred loading. Actually it's already done but I am not really happy with my code. I will wait for CLR4 generics covariance to propose a clean solution.

The whole solution is attached to this post.