0

I am implementing a generic Tree as follow:

I have a Node class defined as:

public class Node<T>
{
    public Node<T>? Parent;
    public readonly Tree<T> Children;
    public T? Data;

    public Node() => Children = new(this);
}

And a Tree class defined as:

public class Tree<T> : List<Node<T>>
{
    protected Node<T>? _owner = null;

    public Tree() { }
    public Tree(Node<T>? owner) => _owner = owner;

    public new void Add(Node<T> node) {
        base.Add(node);
        node.Parent = _owner;
    }
}

Then I have a DerivedData class that will be used to specify the generic type, implemented as:

public interface IDataRow { }

public abstract class BaseData : IDataRow { }

public class DerivedData : BaseData { }

The problem comes when I want to use it.

public class Program
{
    public Program()
    {
        Node<BaseData> node = new Node<DerivedData>(); // This does not compile

        Tree<BaseData> tree = new()
        {
            node
        };
    }
}

The assignment Node<BaseData> node = new Node<DerivedData>(); does not compile because it cannot implicitly convert Node<DerivedData> to Node<BaseData>.

How can I achieve this?

I tried implementing covariant and contravariant interfaces to be implemented by Node class

public interface INodeIn<in T> { }
public interface INodeOut<out T> { }
public interface INode<T> : INodeIn<T>, INodeOut<T> { }

public class Node<T> : INode<T> { ... }

Then rewrite my main rutine as:

public Program()
{
    INodeOut<BaseData> node = new Node<DerivedData>(); // Now this works...

    Tree<BaseData> tree = new()
    {
        node // ...but this fails
    };
}

When I change the data type of Tree.Add(Node<T> node) method to be Tree.Add(INodeOut<T> node), other two new errors appear because INodeOut does not contain a definition of Parent. By adding that definition neither works.

1 Answers1

-1

u can make the Node class covariant in T:

public class Node<out T> 
{
    public T Data {get;set;} 
    //...
}

This will allow you to assign Node<DerivedData> to Node<BaseData> and :

Node<DerivedData> node = new Node<DerivedData>();
Tree<BaseData> tree = new Tree<BaseData>();
tree.Add(node);

Covariance and contravariance in generics

Here is a runnable code At the request of our friend @Enigmativity

public interface IDataRow { }

public abstract class BaseData : IDataRow { }

public class DerivedData : BaseData { }


public interface INode<out T> where T : IDataRow 
{
    INode<T>? Parent { get; }
}

public class Node<T> : INode<T> where T : IDataRow
{
    public INode<T>? Parent { get; set; }
    public readonly Tree<T> Children;
    public T? Data { get; set; }

    public Node()
    {
        Children = new Tree<T>(this);
    }
}

public class Tree<T> : List<INode<T>> where T : IDataRow
{
    public Node<T>? Owner { get; set; }

    public Tree(Node<T>? owner)
    {
        Owner = owner;
    }

    public new void Add(INode<T> node)
    {
        base.Add(node);
        node.Parent = Owner; 
    }
}


public class Program
{
    public static void Main()
    {
        INode<BaseData> baseNode = new Node<DerivedData>();

        Tree<BaseData> tree = new Tree<BaseData>();
        tree.Add(baseNode);

        Console.WriteLine("Tree node: " + tree.Contains(baseNode)); 
    }
}
sep7696
  • 494
  • 2
  • 16