1

I would like to create a linq like extension method to get all items from a hierarchical structure like a tree.

This is my extension

public static List<T> GetAllRecursive<T, TU>(this IList<T> list, Func<T, TU> func) where TU : IEnumerable<T> {
    var allList = new List<T>();
    var toAdd = list.ToList();
    while(true) {
        allList.AddRange(toAdd);
        var childs = toAdd.SelectMany(x => func(x)).ToList();
        if(childs.Count == 0) {
            return allList;
        }
        toAdd = childs;
    }
}

I call it like this

var allGuidelines = Guidelines.GetAllRecursive(x => (IEnumerable<MachineGuidelineTreeItemViewModel>)x.Children);

How can I improve this method so I don't need to cast every time I use this?

Thanks in advance

9
  • What is the return type of Children? Commented Oct 13, 2017 at 7:17
  • and why do you need to cast it to IEnumerable at all? Isn't x.Children an IEnumerable<> already? Is it a more generic type, for example, IEnumerable<object> or something like that? If so, how do you know that it's safe to cast it to IE<MachineGuidelineTreeItemViewModel> ? Commented Oct 13, 2017 at 7:17
  • Children is a BindableCollection<TreeItemViewModel> Commented Oct 13, 2017 at 7:23
  • I need to cast, because the compiler does not understand BindableCollection<T> is a IEnumerable<T> the problem is the generics. Commented Oct 13, 2017 at 7:25
  • 1
    If Children is BindableCollection<TreeItemViewModel> it means it can contain any descendant of TreeItemViewModel, not just MachineGuidelineTreeItemViewModel, so it's not safe to cast it to IEnumerable<MachineGuidelineTreeItemViewModel>. You might know it's safe at runtime but compiler sure cannot. Commented Oct 13, 2017 at 7:32

1 Answer 1

1

You can remove TU type parameter, and use OfType on IEnumerable instead:

public static List<T> GetAllRecursive<T>(this IList<T> list, Func<T, IEnumerable> func) {
    var allList = new List<T>();
    var toAdd = list.ToList();
    while(true) {
        allList.AddRange(toAdd);
        var childs = toAdd.SelectMany(x => func(x).OfType<T>()).ToList();
        if(childs.Count == 0) {
            return allList;
        }
        toAdd = childs;
    }
}

This approach is more lenient, because it lets you pass functions that return a mixed bag of items, with the method filtering it by type.

Sign up to request clarification or add additional context in comments.

3 Comments

I think maybe the toAdd variable and childs variable, while required, do not necessarily have to be Lists. Probably won't make much of a difference, but I'm thinking it's possible to avoid that ToList() call when assigning childs...
@BurnsBA I agree, there's definitely some room for making this method even more general. I went with OP's implementation to address one specific shortcoming, while keeping the rest of his code in place.
thats exactly what i was looking for now i have a small call: var allGuidelines = Guidelines.GetAllRecursive(x => x.Children); - Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.