0

I'm using reflection to get a property that is a ICollection<TestCastChild> and cast it as a ICollection<ICastBase>. TestCastChild implement's ICastBase. When I try to cast the collection, the cast fails. I'm sure that I'm missing something simple. I can't see why this fails.

public interface ICastBase
{
    int Id { get; set; }
}

public interface ICastChild : ICastBase
{
    string Name { get; set; }
}

public abstract class TestCastBase : ICastBase
{
    public int Id { get; set; }
}

public class TestCastChild : TestCastBase, ICastChild
{
    public string Name { get; set; }
}

public class TestCastParent : TestCastBase
{
    public virtual ICollection<TestCastChild> Children { get; set; }
}

Then to test:

[TestMethod]
public void TestCast()
{
    var parent = new TestCastParent();
    parent.Children = parent.Children ?? new List<TestCastChild>();
    parent.Children.Add(new TestCastChild{Name = "a"});
    parent.Children.Add(new TestCastChild { Name = "b"});
    parent.Children.Add(new TestCastChild { Name = "c"});

    var propInfos = parent.GetType().GetProperties();
    foreach (var propertyInfo in propInfos)
    {
        if (propertyInfo.PropertyType.GetMethod("Add") != null)
        {
            var tmpVal = propertyInfo.GetValue(parent);
            //This evaluates to null
            var cast1 = tmpVal as ICollection<ICastBase>; 
            //This evaluates to null
            var cast2 = tmpVal as ICollection<ICastChild>;
            //This evaluates to the expected value
            var cast3 = tmpVal as ICollection<TestCastChild>;
        }               
    }
}
Jeff
  • 2,728
  • 3
  • 24
  • 41
  • Yes, this is a duplicate. After seeing the answer here, I was able to search on 'covariant' and find other answers. – Jeff Jun 29 '15 at 18:26

1 Answers1

6

You cannot cast from ICollection<Derived> to ICollection<Base>, as ICollection<T> is not covariant.

If it were possible, you could cast an ICollection<Dog> to ICollection<Mammal> and then add a Cat to the collection, as it's a Mammal too.

What you can do, is cast from IReadOnlyCollection<Derived> to IReadOnlyCollection<Base> as IReadOnlyCollection<out T> is covariant. If your concrete collection type implements IReadOnlyCollection<out T> (and List<T> does) it will work just fine, but you'll only get a read only interface to the underlying collection. This way, type safety is still preserved.

Note that you can also use IReadOnlyList<out T>, which inherits from IReadOnlyCollection<out T> and it adds an indexer.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158