Question

I was doing some work with interfaces today, when I run into the following scenario. Given these two simple interfaces:

public interface IItem { }
public interface IInventory
{
    ICollection<IItem> Items { get; }
}

I made a simple class to implement IInventory, and noticed that this implementation is perfectly fine as written:

public class BasicInventory1 : IInventory
{
    private Dictionary<int, IItem> items;
    public ICollection<IItem> Items
    {
        get { return items.Values; }
    }
}

But yet, this implementation requires a cast:

public class BasicInventory2 : IInventory
{
    private Dictionary<int, IItem> items;
    public ICollection<IItem> Items
    {
        get { return (ICollection<IItem>)items; }
    }
}

Why does one require a cast and the other doesn't? Checking the object typing for both collections that are getting returned in either case confirms that they both in fact implement ICollection.

I suspect there is some magic type conversions going on under the hood here, and therefore seems to have something to do with co/contravariance, but I don't quite see what exactly is going on.

Was it helpful?

Solution

Dictionary<int, IItem> does not implement ICollection<IItem>. Simple as that.

It wouldn't make sense to implement that interface because you cannot add to a dictionary without specifying a key. The interface does not make sense.

This is a runtime error because items could refer to a subclass of Dictionary so that the cast might be valid.

OTHER TIPS

I think that if you were to add .Values to the second example, you would not need the cast

public class BasicInventory2 : IInventory
{
    private Dictionary<int, IItem> items;
    public ICollection<IItem> Items
    {
       get { return items.Values; }
    }
}

This is because items is a Dictionary and that implements ICollection<KeyValuePair<TKey, TValue>>.

This code is NOT VALID and will always generate a runtime error:

    public class BasicInventory2 : IInventory
    {
        private Dictionary<int, IItem> items = new Dictionary<int, IItem>();

        public ICollection<IItem> Items
        {
            get
            {
                return (ICollection<IItem>) items;
            }
        }
    }

A Dictionary<int, IItem> does NOT implement ICollection<IItem>, whereas the type returned from Dictionary<int, IItem>.Values does.

So the answer is:

The first case is ok because Values is of the correct type.

In the second case, the compiler knows that you are trying to return the wrong type and so it gives you a compile error.

If you override the error with a case, you will get a runtime BadCastException.

In BasicInventory1 you return items.Values in BasicInventory2 you return only items. .Values returns a ICollection, so no cast is need.

MSDN:

In the second code you use the dictionary as return value where in the first code you use the values. Dictionary<int,IItems> inherits from ICollection<KeyValuePair<int,IItems>> thus is not ICollection<IItems>. Therefore you need the cast.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top