Single extension method on IDictionary<K, IEnumerable/IList/ICollection<V>> - c#

I'm trying to write an extension method that will convert IDictionary<K, S<V>> holding any type of collection/sequence (S<V>) to ILookup<K, V> which is more proper data structure in those cases. This means I'd like my extension to work on different S types and interfaces:
IDictionary<K, IEnumerable<V>>
IDictionary<K, ICollection<V>>
IDictionary<K, List<V>>
etc. Ideally, I don't want to write separate implementation for each possible collection type AND I want type inference to do its job.
What I've tried is:
public static ILookup<TKey, TValue>ToLookup<TKey, TCollection, TValue>(
this IDictionary<TKey, TCollection> dictionary)
where TCollection : IEnumerable<TValue>
But it have no TValue in parameters list, so type inference is unable to figure it out - I get "The type arguments for method ToLookup cannot be inferred from the usage".
Is there a chance it could work somehow in other way than adding fake TValue-typed parameter to the method?
Examples of expected usage
I hope all above calls to be possible and result in a call to my single extension method:
var dictOfIEnumerables = new Dictionary<int, IEnumerable<int>>();
var lookupFromIEnumerables = dictOfIEnumerables.ToLookup();
var dictOfICollections = new Dictionary<int, ICollection<int>>();
var lookupFromICollections = dictOfICollections.ToLookup();
var dictOfLists = new Dictionary<int, List<int>>();
var lookupFromLists = dictOfLists.ToLookup();

Because all collections implement IEnumerable<T>, we can just use it instead of the TCollection type paramter. Unfortunately the type inference does not know this. This is the code I wrote:
public static ILookup<TKey, TValue> ToLookup<TKey, TValue>
(this IDictionary<TKey, IEnumerable<TValue>> dict)
{
return dict.SelectMany(p => p.Value.Select
(v => new KeyValuePair<TKey, TValue>(p.Key, v)))
.ToLookup(p => p.Key, p => p.Value);
}
There seems to be no way of making the type inference work, but this method will work if you cast the Dictionary:
((IDictionary<int, IEnumerable<int>>)dictOfLists).ToLookup()
Also you can add Lists to a Dictionary of IEnumerables and cast them back when you need them.

From the bit of testing I've done, here are my results.
If I type dictOfIEnumerables.ToLookup(, I see 4 overloaded methods.
However, if I type dictOfIEnumerables.ToLookup<, I see all 5 overloaded methods.
It appears that type inference isn't working, because of name collision/resolution collision between the ToLookup() that is defined on IEnumerable. Apparently, without the angle brackets, it's resolving to the methods defined on IEnumerable, because that is what TCollection is restricted to. Maybe someone on StackOverflow that is smarter than I can explain to you why it works the way it does.
However, using the specified types does, in fact, work correctly on my machine.

Related

.NET Reflection - Results of GetInterfaces() of generic type definition cannot be successfully compared to generic typeof literal

I recently had to do some reflection in .NET and stumbled upon a strange behavior while comparing generic type definitions.
I had to decide whether a type is an IDictionary<,> by extracting a Type object from a LINQ Expression so I was unable to use the is operator to decide it. Additionally, I had to ignore the type parameters, in other words, the only thing that was important is whether I was dealing with an IDictionary<,> of any key/value type.
I started out with the following:
// Just a placeholder, in my code I have no way of knowing that it
// is a Dictionary of string keys and string values or even that it
// is a dictionary at all.
typeof(Dictionary<string, string>)
.GetGenericTypeDefinition()
.GetInterfaces()
.Any(i => i == typeof(IDictionary<,>))
I assumed that since I was reading the interfaces of a generic type definition I would get generic interface definitions. Even more strange is that when I pasted the above code to LINQPad it returned the following:
typeof(IDictionary<TKey,TValue>) // it appears here
typeof(ICollection<KeyValuePair<TKey,TValue>>)
typeof(IEnumerable<KeyValuePair<TKey,TValue>>)
typeof(IEnumerable)
typeof(IDictionary)
typeof(ICollection)
typeof(IReadOnlyDictionary<TKey,TValue>)
typeof(IReadOnlyCollection<KeyValuePair<TKey,TValue>>)
typeof(ISerializable)
typeof(IDeserializationCallback)
Yet for some reason the comparison in the Any method did not succeed for any of these elements. However, if I get the generic type definitions for the interfaces themselves like so:
typeof(Dictionary<string, string>)
.GetGenericTypeDefinition()
.GetInterfaces()
.Select(i => i.IsGenericType ? i.GetGenericTypeDefinition() : i)
.Any(i => i == typeof(IDictionary<,>))
then it returns true like it should.
Why is that? Aren't the interfaces returned by the .GetInterfaces() method when called on a generic type definition generic interface definitions themselves already? If so, what's the explanation that when I inspect the returned interfaces in LINQPad they appear to be generic type definitions?
I guess it has something to do with the fact that the interface's type parameters could be completely independent from the implementer's generic arguments, e.g.:
public class MyGenericType<TKey, TValue> : ISomeInterface<object> ...
but still it looks strange that the returned Type objects don't seem to reflect such a case (as I'd expect in this specific example) and from the 'looks' they appear to be generic type definitions yet they aren't identified as that.
CodeCaster already gave the answer in the comments section. Basically, it is related to open/closed constructs. typeof(IDictionary<,>) is not the same as typeof(IDictionary<string,int>). Moreover it is not the same as typeof(IDictionary<TKey,TValue>).
Basically, typeof(IDictionary<,>) is an unbound type but IDictionary<TKey, TValue>, which you get from your typeof(Dictionary<string, string>).GetGenericTypeDefinition() is an open but constructed type since TKey and TValue are type parameters.
So here is a simple extension method which can do what you want.
public static bool HasGenericInterface(this Type #type,Type genericInterface)
{
var allInterfaces = #type.GetInterfaces();
return allInterfaces
.Where(i => i.IsGenericType)
.Select(i => i.GetGenericTypeDefinition())
.Any(i => i.IsAssignableFrom(genericInterface));
}
Simple usage is as follows
var result = typeof(MyClass).GetType().HasGenericInterface(typeof(IDictionary<,>));

C# method selection chooses object instead of IEnumerable<object> when passing a primitive type collection

I have an object with two add methods:
public void Add(string keyName, object value)
public void Add(string keyName, IEnumerable<object> values)
If you call it and provide an object, it calls the first one. If you provide an IEnumerable it calls the second one. However if you provide an IEnumerable of a primative type, it calls the first one.
Is there a way to make c# choose the 2nd method and box it rather than treat the array as an object?
If not I guess I will put a check in the first add method to see if it's a primitive type ienumerable and force it to call the second method.
You can use generics for this. If you change the second method to:
public void Add<T>(string keyName, IEnumerable<T> values)
{
}
Then if it is any kind of IEnumerable<T> (eg IEnumerable<int>) then it will use the generic method.
It is worth noting that IEnumerable<T> is not exactly the same as IEnumerable<object> so if you need it to be an IEnumerable<object> you would need something like IEnumerable<object> obList = values.Cast<object>();
To add to #Chris answer and explain why it does not work with your way. IEnumerable<T> defined like this:
public interface IEnumerable<out T>
Where out means type T is covariant, which in turn means you can use child type in it's place. In your case it means you can use any type that inherits from object where IEnumerable<object> is expected, such as IEnumerable<string>. BUT those covariance modifiers (out\in) do not work with value types (structs). That means IEnumerable<string> is convertible to IEnumerable<object> but IEnumerable<int> does not. So compiler cannot chose second overload because it will just not compile.
The right way to solve that as already been answered is to use generics (you can also explicitly cast IEnumerable<int> with en.Cast<object>()).

Why can types not be inferred from extension method usage?

See the following recent answer:
https://stackoverflow.com/a/36421418/1017882
To summarise, an extension method has been written that uses generics:
static TResult GetEntry<TEnum, TResult>(this Dictionary<TEnum, string> dictionary, TEnum key) {...}
All works fine when invoked like so:
var attributes = new Dictionary<MyTestEnum, string>();
var result = attributes.GetEntry<MyTestEnum, double>(MyTestEnum.First);
But if I invoke it differently:
var result = attributes.GetEntry(MyTestEnum.First);
Apparently the compiler can no longer infer the types. But it's very clear to see (based on what attributes is), what type I'm attempting to pass.
Why must I be explicit with what types I pass? It feels like unnecessary code.
Usually questions like this are answered with example usage that would 'break' without this kind of extra info - but I can't think of any scenarios like this.
Bit More Context
The reason I even tried drop that bit of syntax in the first place is because I (vaguely) recall being able to do so with certain generic uses. I wanted to see how it applies with collections like this.
But it's very clear to see (based on what attributes is), what type I'm attempting to pass.
But not how you are trying to pass it.
TResult does not depend on any of the types passed, so there's no way of knowing you want GetEntry<MyTestEnum, double> and not GetEntry<MyTestEnum, int> or GetEntry<MyTestEnum, MyTestEnum> or what have you.
There are times when a human using wider context than the compiler uses can see what is meant even when the compiler can't infer it, but this isn't one of those cases. I can only guess you probably want GetEntry<MyTestEnum, double> because that's the example you use earlier in your question.
If on the other hand you had something like:
public static TResult GetEntry<TKey, TResult>(Dictionary<TKey, TResult> dict, TKey key)
{
return dict[key];
}
Then because all the type parameters relate to part of the signature of what it is called with, it would indeed be inferable, though there are still subtler cases where such inference can't happen.

What do nested generics in C# mean?

A bit of a basic question, but one that seems to stump me, nonetheless.
Given a "nested generic":
IEnumerable<KeyValuePair<TKey, TValue>>
Is this stating that IEnumerable can have generic types that are themselves KeyValuePair 's ?
Thanks,
Scott
Yes. The KeyValuePair type expects two generic type parameters. We can either populate them by pointing to concrete types:
IEnumerable<KeyValuePair<string, int>>
Or we can populate them by using other generic parameters already specified by the outer class:
class Dictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
Generic type parameters are always specified "at-use", or at the point where you are using the class or method that requires them. And just like any other parameter, you can fill it with a constant, hard-coded value (or type in this case), or another variable.
Yes, this is "An IEnumerable of Key/Value pairs." It would be declared thusly:
IEnumberable<KeyValuePair<string, string>> reallyComplicatedDictionary =
new IEnumerable<KeyValuePair<string, string>>();
Or similar.
About the only think I can think this particular usage would do is allow you to have a "dictionary" with repeated keys.
In a nut shell, it means that when you enumerate over that IEnumerable, you're going to get KeyValuePair<TKey, TValue> (for whatever types TKey and TValue are set to).
So, yes.
Here
IEnumerable<KeyValuePair<string, int>>
The IEnumerable itself is not a generic. It knows that it is going to contain KeyValuePair. Here KeyValuePair is the generic which can contain any 2 generic types.

List<T> Ordering

I have an issue, I am allowing a user to select the criterea for ordering a List
Lets say my list is called
List<Cars> AllCars = new List<Cars>;
allCars = //call the database and get all the cars
I now want to order this list
allCars.orderBy(registrationDate)
I understand the above doesn't work but i haven't anyidea what i should be putting in the brackets.
allCars.OrderBy(c => c.RegistrationDate);
I understand the above doesn't work but i haven't anyidea what i should be putting in the brackets.
The declaration of Enumerable.OrderBy is
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
and, as it's an extension method it can be invoked as
source.OrderBy(keySelector).
Your List<Car> is playing the role of source as List<T> : IEnumerable<T>. The second parameter is the more interesting one and the one that you are confused by. It's declared as being of type
Func<TSource, TKey>
This means that it is a delegate that eats instances of TSource (in your case Car) and returns an instance of TKey; it's up to you to decide what TKey is. You have stated that you want to order by Car.registrationDate so it sounds like TKey is DateTime. Now, how do we get one of these delegates?
In the old days we could say
DateTime GetRegistrationDate(Car car) {
return car.registrationDate;
}
and use OrderBy like so:
allCars.OrderBy(GetRegistrationDate).
In C# 2.0 we gained the ability to use anonymous delegates; these are delegates that don't have a name and are defined in-place.
allCars.OrderBy(delegate(Car car) { return car.registrationDate; });
Then, in C# 3.0 we gained the ability to use lambda expressions which are very special anonymous delegates with a compact notation
allCars.OrderBy(car => car.registrationDate);
Here, c => c.registrationDate is the lambda expression and it represents a Func<Car, DateTime> than can be used the second parameter in Enumerable.OrderBy.
allCars.orderBy(registrationDate)
The reason this doesn't work is because registrationDate is not a delegate. In fact, without any context at all registrationDate is meaningless to the compiler. It doesn't know if you mean Car.registrationDate or maybe you mean ConferenceAttendee.registrationDate or who knows what. This is why you must give additional context to the compiler and tell it that you want the property Car.registrationDate. To do this, you use a delegate in one of the three ways mentioned above.

Categories

Resources