-1

Take the following example:

class Foo {
    public int SomeProperty { get; }
    public IEnumerable<Foo> Children { get; }
    // ....
}

If another class (e.g. Bar) implements Foo, Foo becomes non-recursive and Bar instead becomes recursive.

class Bar {
    public Foo { get; } // At this point Foo.Children doesn't make sense anymore
    public IEnumerable<Bar> Children { get; } // How can I enforce this part
    // ...
}

Currently I need to write two versions of every class, one with Children and one without. Inheritance makes this slightly less tedious but I can't statically guarantee that the requirements will be implemented correctly. I was also thinking about some kind of monad pattern with a higher kinded type Recursive but I can't wrap my head around it (especially since I just started learning it).

Any ideas?

Edit: I don't understand how this got marked as a duplicate. While I ended up with writing my own generic tree in the end, I didn't require it in my question and definitely didn't ask for a library. I was asking for general advice on how to approach the problem, where the solution I settled with ended up being a generic tree by chance. The fact that this question got closed after I provided my solution leads me to believe that someone read the answer, searched for trees on SO and then chose the first result, which doesn't help anyone. The "original" question doesn't even provide a solution to my question. This nothing more than laziness paired with the abuse of admin rights.

CookedCthulhu
  • 744
  • 5
  • 14
  • 2
    How does `Bar`"implement" `Foo` here? – Fildor Jan 23 '19 at 09:11
  • There's no such thing in C# like "statically alter a parent class when there exists a child class with specific properties". – Wiktor Zychla Jan 23 '19 at 09:12
  • I really can't understand what are you aiming for – Marco Salerno Jan 23 '19 at 09:14
  • C# doesn't support this kind of variance -- the type of `Children` cannot change while inheriting. If you don't need inheritance, you can use `interface IFoo where T : IFoo { IEnumerable Children { get; } }` and then have `Foo` and `Bar` implement `IFoo` and `IFoo`, respectively. You *can* actually combine this with inheritance if you use explicit implementations, but that gets a little messy. – Jeroen Mostert Jan 23 '19 at 09:16
  • Probably can't do this. Similar question: https://stackoverflow.com/q/8727523 – Dialecticus Jan 23 '19 at 09:24

1 Answers1

-1

I figured it out:

Don't make any of the classes recursive. Instead, create a generic class Recursive<T>:

public class Recursive<T>
{
    public T Head { get; }
    public IEnumerable<Recursive<T>> Children { get; }

    public Recursive(T head, IEnumerable<Recursive<T>> children)
    {
        Head = head;
        Children = children;
    }
    public Recursive(Recursive<T> source)
    {
        Head = source.Head;
        Children = source.Children;
    }
}

Next we need a way to transform Recursive<Foo> to Recursive<Bar>, which is pretty simple to implement (I used an extension method because this idea borrows from IEnumerable and functional programming in general; it just seemed more fitting):

public static Recursive<U> Select<T, U>(this Recursive<T> source, Func<T, U> selector)
{
    var head = selector(source.Head);
    var children = source.Children.Select(x => Select(x, selector));
    return new Recursive<U>(head, children);
}

And that's almost it. As long as you can force the very first construction to be Recursive<Foo> instead of just Foo, the issue should be solved. This part is probably implementation specific. I made the constructors of Foo private and instead provided a RawFoo class, plus a few construction methods from IEnumerable<RawFoo> to Recursive<Foo>/IEnumerable<Recursive<Foo>>.

If another object needs Foo, it can only get Recursive<Foo> easily. Sure, someone could call myFoo.Head but it's pretty clear that they're probably not getting all of the information they want. Instead, it's a lot easier to call Select, like the following example, which provides the programmer with an implementation of Recursive<Bar> instead of Bar:

var myBar = myFoo.Select(x => {
    var someBarProperty = // ...
    return new Bar(x, someBarProperty);
}
// typeof(myBar): Recursive<Bar>
// typeof(myBar.foo): Foo
CookedCthulhu
  • 744
  • 5
  • 14