-1

So I'm running into a problem with generic lists in C# such that:

var foo = new Dictionary<long, List<object>>();
foo.Add(123, new List<string>());

Gives the error: Cannot convert from System.Collections.Generic.List<string> to System.Collections.Generic.List<object>

Where as this works fine:

var foo = new Dictionary<long, object>();
foo.Add(123, new string());

My real use case would look like this:

var foo = new Dictionary<long, List<ISomeInterface>>();
foo.Add(123, new List<SomeClassA>());
foo.Add(345, new List<SomeClassB>());

Where both SomeClassA and SomeClassB implement ISomeInterface. Is this possible?

The idea being I can grab a list of ISomeInterface from the dictionary and iterate the list calling SomeMethod that is defined on ISomeInterface.

Shanerk
  • 5,175
  • 2
  • 40
  • 36
  • 6
    What you want is covariance and that is only available with interfaces. So `Dictionary>` would work, but of course you then can only take items out of values of the dictionary, but that's to make sure you don't put a string into what is actually a list of int. – juharr Jul 18 '18 at 16:53
  • Not sure if this would help in your case, but you can declare a list of type `List` and populate it with instances of `SomeObject`, and then add it to the `Dictionary` – Rui Jarimba Jul 18 '18 at 16:56
  • If you need to know the concrete type of an interface that's a code smell. You should push any class type logic like that into the classes via the interface's API. – juharr Jul 18 '18 at 16:59
  • What @juharr said is correct, but you could also use `Dictionary>` – Michal Ciechan Jul 18 '18 at 17:15

3 Answers3

0

For your real use case:

You need to declare your list as List<ISomeInterface> and then fill it with objects that implement such interface.

var foo = new Dictionary<long, List<ISomeInterface>>();
foo.Add(123, new List<ISomeInterface>());
foo[123].Add(new SomeObjectThatImplementsISomeInterface());

Other way would be to create the list first, then add it to the dictionary.

var bar = new List<ISomeInterface>();
bar.add(new SomeObjectThatImplementsISomeInterface());
bar.add(new SomeOtherObjectThatImplementsISomeInterface());
var foo = new Dictionary<long, List<ISomeInterface>>();
foo.Add(123, bar);

Of course when you get back values from foo you'll get them as ISomeInterface, and will need reflection if you need to know the specific type of such object.

Josh Part
  • 2,154
  • 12
  • 15
  • reflection or just `.Cast<>()` – Pablo Recalde Jul 18 '18 at 17:00
  • @r1verside Better yet is to push that type of logic into the classes via the interface API so you don't need reflection or casting. – juharr Jul 18 '18 at 17:02
  • @r1verside what I understood in OP's comments is that he needs to know the type. If he knows or expects a specific one he can cast it but as juharr mentions that's not realley what interfaces are for. – Josh Part Jul 18 '18 at 17:06
  • I need to know the Interface type so I can call the shared methods when I iterate the list. This answer seems best with one change, instead of `foo[123].Add()` do `foo[123].AddRange()` – Shanerk Jul 18 '18 at 17:07
  • @Shane if you already have the list, then just add it to the dictionary instead of creating then populating it. I'll update my answer on that. – Josh Part Jul 18 '18 at 17:09
  • @Josh, your original answer put me on the right path, I'll update my question to be more explicit as your second part is a little different use case. – Shanerk Jul 18 '18 at 17:14
0

You can use ToList for casting the class to the generic type interface as in

using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp9
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<int, List<ISomething>> dictionary
                = new Dictionary<int, List<ISomething>>();

            var list = new List<Something>() { new Something() };
            dictionary.Add(1, list.ToList<ISomething>());
        }

        public interface ISomething
        {
        }

        public class Something : ISomething
        {
        }
    }
}
Aleks Andreev
  • 7,016
  • 8
  • 29
  • 37
Dan Hunex
  • 5,172
  • 2
  • 27
  • 38
  • 1
    Thank you. `ToList()` while perhaps obvious to some, was exactly what I needed and the simplest solution. – Shanerk Jul 20 '18 at 00:08
0

You can get around it with System.Linq:

var foo = new Dictionary<long, List<object>>();
foo.Add(123, new List<string>().ToList<object>());

similarly,

var foo = new Dictionary<long, List<ISomeInterface>>();
foo.Add(123, new List<SomeObject>().ToList<ISomeInterface>());

another possibility:

foo.Add(123, new List<ISomeInterface>(new List<SomeObject>()));
Cosmin Sontu
  • 1,034
  • 11
  • 16