Nested generics with IDictionary and IEnumerable

In a general C # class for an internal reusable library, I would like to pass a link to "something that maps to other things." The data types of what is passed into them should not be known to the library. In addition, it should not be known how they are stored, that is, what is today a list stored in memory can later be a database table, which is read on demand.

So, I thought I would write this library class:

class GenericClass<T, U> { public void Foo(IDictionary<T, IEnumerable<U>> bar) { // do something } } 

This compiles, but trying to pass specific implementations does not:

 class UsingClass { public static void Main(string[] args) { var c = new GenericClass<string, string>(); c.Foo(new Dictionary<string, List<string>>()); } } 

I get the following two syntax errors:

 Filename.cs(46,13): error CS1502: The best overloaded method match for 'GenericClass<string,string>.Foo(System.Collections.Generic.IDictionary<string,System.Collections.Generic.IEnumerable<string>>)' has some invalid arguments Filename.cs(46,19): error CS1503: Argument 1: cannot convert from 'System.Collections.Generic.Dictionary<string,System.Collections.Generic.List<string>>' to 'System.Collections.Generic.IDictionary<string,System.Collections.Generic.IEnumerable<string>>' 

Replacing IEnumerable with a Foo() declaration on List fixes it, but this, of course, is not quite what I want.

Is this really not supported by C # (4.0), or am I just missing something obvious? What workaround would you suggest? (I'm sure they talked a lot about this, so the links to great descriptions are great too.)

Yes, I should be able to write my own helper classes for myself, but why should I?

+4
source share
1 answer

Yes, it really is not supported. Imagine your Foo method looked like this:

 public void Foo(IDictionary<T, IEnumerable<U>> bar) { T key = GetKeyFromSomewhere(); bar[key] = new U[10]; // Create an array } 

It looks good, right? We can convert from U[] to IEnumerable<U> .

This is not so good from the point of view of the caller, though - suddenly we have a string[] reference value in the dictionary, when all the values ​​are intended for a List<string> link! The explosion is in a safe type.

You can rewrite the method as follows:

 public void Foo<TValue>(IDictionary<T, TValue> bar) where TValue : IEnumerable<U> 

This will allow you to get the values ​​from the dictionary and implicitly convert them to IEnumerable<U> ... but you can insert exactly the right type of value into the dictionary, and you can’t only use the value of U

Starting with version 4, C # supports general dispersion in limited circumstances. So, for example, this works in C # 4 (when targeting .NET 4), but it wasn’t before:

 List<string> strings = new List<string>(); IEnumerable<object> objects = strings; 

For more information on total variance, see Eric Lippert's blog series section. Be prepared for your brain to explode periodically.

+8
source

Source: https://habr.com/ru/post/1347132/


All Articles