-1

During the project (.NET Framework 'I belive' 4.6.2) at work i meet interesting and confusing behavior:

var dict = new Dictionary<string, Dictionary<string, SomeFunnyStuff>>();
// Dictionary hard working collector

this funny dict was past to the class but as programmer learn some years ago: 'You should always base your classes on abstractions' (that was voice in my head ^_^) I create class like so:

internal class SampleStackoverflowClass
{
    public IDictionary<string, IDictionary<string, SomeFunnyStuff>> TransformationDictionary { get; }

    // Just for simplify
    public SampleStackoverflowClass(IDictionary<string, IDictionary<string, SomeFunnyStuff>> transformation)
    {
        TransformationDictionary = transformation;
    }

    // some extremely enterprise logic ... 
}

Ant them when we wanna pass the dictionary:

var sample = new SampleStackoverflowClass(dict);

We got:

Argument 1: cannot convert from System.Collections.Generic.Dictionary>' to 'System.Collections.Generic.IDictionary>

What seams weird becouse we got covariance and contravariance in C# 4.0 if I am not wrong.

So i come back to home after that stunning (at least for me :-)) discover start testing even the totally basic staff like (It works):

IEnumerable<IEnumerable<IEnumerable<object>>> working = new List<List<List<string>>>();

and after that create .NET Core (3.1) project and test gives the same results which feels odd.

So my questions:

  • I this normal behavior or lack of tests? (Not to much people create Dictionary of Dictionary ^_^)
  • If it's normal can someone explain the bright idea behind it? (Meaning Why it is not allow? maybe some MSIL problems Or CLR ones I belive someone can rationally explain that if that was planed behavior)

Thanks for time spending 4

F0urth
  • 66
  • 5
  • _"we got covariance and contravariance in C# 4.0 if I am not wrong"_ -- it is correct that C# has type variance for generic interfaces. But type variance doesn't mean you get to mix things willy-nilly. It still needs to be safe, and in your example, it's not safe because the dictionary types are read/write, and so can be neither co- nor contra-variant. See marked duplicate, and the hundreds of similar questions, for more specifics as to why this is. – Peter Duniho Mar 03 '20 at 23:45

1 Answers1

2

Your "non-interface" dict is stricter than the interfaced one. So they're not interchangeable. This is easier to talk about if we simplify the types a bit.

interface IFoo {}
class Foo : IFoo {}
// ... //

// This is fine. A List is an IList, and every IFoo is an IFoo.
IList<IFoo> myList1 = new List<IFoo>();
// This is fine as well, obviously.
List<Foo> myList2 = new List<Foo>();

// Not fine - an IList might not be a list. Can't be automatically assigned.
List<IFoo> myList3 = myList1;
// Not fine - myList2 can only contain Foo objects, but myList4 should be able to contain any IFoo object.
List<IFoo> myList4 = myList2;

In your code, an Dictionary<string, Dictionary<string, SomeFunnyStuff>> can not do the same job as a IDictionary<string, IDictionary<string, SomeFunnyStuff>>. The interface one must accept any value of the IDictionary<string, SomeFunnyStuff> type. But the concrete one can only accept values of the type Dictionary<string, SomeFunnyStuff>.

e.g.

IDicationary<string,SomeFunnyStuff> someDictFromSomewhere = GetOtherDict();
    var dict = new Dictionary<string, Dictionary<string, SomeFunnyStuff>>();
dict.Add("someKey", someOtherDictFromSomewhere); // OOPS - won't compile. IDictionary<K,V> might not be a Dictionary<K,V>.

Change your dict to be a Dictionary<string,IDictionary<string,SomeFunnyStuff>>, and all is well.

gnud
  • 77,584
  • 5
  • 64
  • 78