Anthony Nichols Anthony Nichols - 2 months ago 6
C# Question

Convert from class to Interface with a generic type interface Interface<Interface>

I am having trouble down-converting a class to it's base interface. I have the following (simplified):

public interface ITaxonomy
{
string CommonName { get; set; }
string ScientificName { get; set; }
}

public interface ITaxonomyHasChildren<TChild> : ITaxonomy where TChild : ITaxonomy
{
ICollection<TChild> Children { get; set; }
}

public interface ITaxonomyHasParent<TParent> : ITaxonomy where TParent : ITaxonomy
{
TParent Parent { get; set; }
}


And I need to extract the data back out in my VM:

private void SelectedEntityChanged(ITaxonomy selectedEntity)
{
CommonName = selectedEntity.CommonName;
ScientificName = selectedEntity.ScientificName;

Children = selectedEntity.GetType().GetProperty(nameof(ITaxonomyHasChildren<ITaxonomy>.Children))?
.GetValue(selectedEntity) as ICollection<ITaxonomy>;

Parent = selectedEntity.GetType().GetProperty(nameof(ITaxonomyHasParent<ITaxonomy>.Parent))?
.GetValue(selectedEntity) as ITaxonomy;
}


The
Children
part doesn't work, I am assuming because it doesn't like converting from
ICollection<MyClass>
to
ICollection<ITaxonomy>


How can I accomplish this?

Answer

In order to make it work, you need to make your generic interfaces covariant. But since ICollection<T> is not covariant, it would require also changing the Children type to IReadOnlyCollection<TChild> and remove the setters if you can afford that.

public interface ITaxonomyHasChildren<out TChild> : ITaxonomy where TChild : ITaxonomy
{
    IReadOnlyCollection<TChild> Children { get; }
}

public interface ITaxonomyHasParent<out TParent> : ITaxonomy where TParent : ITaxonomy
{
    TParent Parent { get; }
}

This will also allow you to avoid reflection:

private void SelectedEntityChanged(ITaxonomy selectedEntity)
{
    CommonName = selectedEntity.CommonName;
    ScientificName = selectedEntity.ScientificName;

    Children = (selectedEntity as ITaxonomyHasChildren<ITaxonomy>)?.Children;
    Parent = (selectedEntity as ITaxonomyHasParent<ITaxonomy>)?.Parent;
}

A sample class implementation:

class Taxonomy : ITaxonomy, ITaxonomyHasChildren<Taxonomy>, ITaxonomyHasParent<Taxonomy>
{
    public string CommonName { get; set; }
    public string ScientificName { get; set; }
    public Taxonomy Parent { get; set; }
    public List<Taxonomy> Children { get; set; } = new List<Taxonomy>();
    IReadOnlyCollection<Taxonomy> ITaxonomyHasChildren<Taxonomy>.Children => Children;
}
Comments