2

Writing code with implicit cast generates CS0266.

Dictionary<Int32, string[]> d1 = new Dictionary<int, string[]>();
IDictionary<int, IEnumerable<string>> ide1 = d1; // CS0266

The hint that an explicit cast exists suggests an explicit cast would fix the issue.

IDictionary<int, IEnumerable<string>> ide1 = (IDictionary<int, IEnumerable<string>>)d1; // No error, but throws an exception

Is there a way to make this cast ? Would it work for string[][] and IEnumerable<IEnumerable<string>> ?

namespace Quickie
{
    class QuickieArray
    {
        private static void TestCastDictionaryWithArrayValue()
        {
            Console.WriteLine();
            Console.WriteLine("===TestCastDictionaryWithArrayValue===");
            Dictionary<Int32, string[]> d1 = new Dictionary<int, string[]>();
            d1[1] = new string[]{"foo"};
            IDictionary<int, string[]> id1 = d1;
            Console.WriteLine("id1 is null: {0}", id1 == null);
            IDictionary<int, IEnumerable<string>> ide1 = id1 as IDictionary<int, IEnumerable<string>>;
            IDictionary<int, IEnumerator<string>> iden1 = id1 as IDictionary<int, IEnumerator<string>>;
            Console.WriteLine("ide1 is null: {0}", ide1 == null);
            Console.WriteLine("iden1 is null: {0}", iden1 == null);

            Console.WriteLine();
        }
        internal static void Test()
        {
            Console.WriteLine();
            Console.WriteLine("=QuickieArray=");
            TestCastDictionaryWithArrayValue();
        }
    }
}

Output:

===TestCastDictionaryWithArrayValue===
id1 is null: False
ide1 is null: True
iden1 is null: True

WaffleSouffle
  • 3,293
  • 2
  • 28
  • 27
  • Quick note: `System.Int32` and `int` are the same type; in C#, the latter is an alias for the former. (So of course `id1` isn't `null`.) – dlev Dec 14 '11 at 18:05
  • You may be interested in my recent answer to a different question for a little more information about the legality of explicit casts. http://stackoverflow.com/a/8402861/55943 – mqp Dec 14 '11 at 18:18

4 Answers4

7

This is a classic covariance/contravarianace problem. I'm going to show you a simplified example to prove why a Dictionary<Int32, string[]> is not a IDictionary<int, IEnumerable<string>> and cannot be cast to one.

Let's pretend for a moment that the cast in question works and this code compiles:

Dictionary<int, string[]> arrayDictionary = new Dictionary<int, string[]>();
IDictionary<int, IEnumerable<string>> enumerableDictionary = arrayDictionary;

Now, we have one object. It's clearly of type Dictionary<int, string[]>. We have two references to it, one being of type Dictionary<int, string[]> and one of type IDictionary<int, IEnumerable<string>>.

What would happen if this code followed?

List<string> stringList = new List<string>();
enumerableDictionary.Add(0, stringList);

Provided that we could cast the Dictionary<int, string[]> in the way you would intend, the above snippet would compile successfully...

...and then blow up at runtime. Why? Because a List<string> is not a string[].

The enumerableDictionary's Add method accepts any IEnumerable<string>, but the real object that was created (the Dictionary<int, string[]>) has a method that only accepts string[].

That is why a Dictionary<Int32, string[]> is not a IDictionary<int, IEnumerable<string>> and cannot be cast to one.

Adam Maras
  • 26,269
  • 6
  • 65
  • 91
  • that's true in order for it not to blow up you would need to do the following – MethodMan Dec 14 '11 at 18:39
  • I agree this is a co/contra-variance problem, and was lazy not stating that. The error message just gave a glimmer of hope: _Cannot implicitly convert type 'type1' to 'type2'. An explicit conversion exists (are you missing a cast?)_. Usage is in a read-only scenario where this is less of an issue. I actually expected it not to work at all. The message made me wonder if it implicitly cast IEnumerable back to array where possible, or created an array by consuming the IEnumerable otherwise, or some other trick (none of which I realise would be a great idea). What's the error message on about ? – WaffleSouffle Dec 15 '11 at 11:27
6

No there is not a cast, nor should there be. Imagine the following:

        Dictionary<Int32, string[]> d1 = new Dictionary<int, string[]>();
        IDictionary<int, IEnumerable<string>> ide1 = d1; 
        ide1.Add(99,new List<string>());

While the third line is perfectly acceptable for an IDictionary<int, IEnumerable<string>>, it is not acceptable for an IDictionary<int, string[]>.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
1

As @adam-maras and @d-stanley stated, this is a covariance problem. Therefore, wherever possible use interfaces. For example, use IDictionary<Int32, IEnumerable<string>> as the less-derived type holding the data instantiated with a more-derived type:

using System;
using System.Collections.Generic;

namespace Quickie
{
    class QuickieArray
    {
        private static void TestCastIDictionaryWithArrayValue()
        {
            Console.WriteLine();
            Console.WriteLine("===TestCastIDictionaryWithArrayValue===");
            IDictionary<int, IEnumerable<string>> d1 = new Dictionary<int, IEnumerable<string>>();
            d1[1] = new string[] { "foo" };
            IDictionary<int, IEnumerable<string>> id1 = d1;
            Console.WriteLine("id1 is null: {0}", id1 == null);
            IDictionary<int, IEnumerable<string>> ide1 = id1 as IDictionary<int, IEnumerable<string>>;
            IDictionary<int, IEnumerator<string>> iden1 = id1 as IDictionary<int, IEnumerator<string>>;
            Console.WriteLine("ide1 is null: {0}", ide1 == null);
            Console.WriteLine("iden1 is null: {0}", iden1 == null);

            Console.WriteLine();
        }

        internal static void Test()
        {
            Console.WriteLine();
            TestCastIDictionaryWithArrayValue();
        }
    }
}

And as far as an explicit cast existing ? No it doesn't.

WaffleSouffle
  • 3,293
  • 2
  • 28
  • 27
0

something like this would work too just as an example in my example I am reading from a comma delim field

var intFileCount = 0;
Dictionary<int, string[]> dctListRecords = null; 
//initialize a capacity if you want 
dctListRecords = new Dictionary<int, string[]>(strLinesStockRecord.Count());
foreach (string strLineSplit in strLinesStockRecord)
{
  lstSplitList2 = strLineSplit.Split(chrCOMMA_SEP).ToList();
  dctListRecords.Add(intFileCount, lstSplitList2.ToArray());
  //this allows you to use the string value inside of string[]
  lstSplitList2.Clear();
  intFileCount++;
}
MethodMan
  • 18,625
  • 6
  • 34
  • 52