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.
Related
I have following statement:
private delegate ITask<Id> CreateObjectDelegateAsync<in T>(T #object)
where T : Model.Object;
And I want to store that in a dictionary:
Dictionary<Type, CreateObjectDelegateAsync<Model.Object>>
Now I am getting errors since T is not covariant and he can't convert it. I have multiple delegates with a derived T that need to be inside that dictionary.
T must be invariant in order for it to be usable as a type in the parameters. Any ideas or workarounds for this?
So the solution was quite simple: While I can't do it with invariance here directly, I can just use a:
Dictionary<Type, Delegate>
instead. I can assign any delegate to that no matter the generic type. Then later when getting the delegate I will just need to cast it to
CreateObjectDelegateAsync<T>
again.
Like so (given that T is an existing type in the dictionary):
(CreateObjectDelegateAsync<T>)DelegateDictionary[typeof(T)]
What's the best way to implement a chained hashtable in C#? For example, the class would be declared like this:
DynamicHashtable<Tkey, Tvalue>
{
....methods
}
Where Tkey is the type of the key stored in the table, and TValue is the type of the values associated with the keys. Thus, a hashtable whose keys are strings and whose associated values are integers would be
DynamicHashtable<string, int>
You can use Hashtable class.
If you wish to see details of implementation Hashtable you can review Source Code here.
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.
I am trying to sort a generic list, and am getting a InvalidOperationException error
Does anyone has suggestions on how to rectify it?
List<XYZ<String, String>> list is being passed on as a parameter through a function.
func( List<XYZ<String, String>> PassedList) {
PassedList.Sort();
}
Any suggestion would be really helpful.
Your XYZ should be IComparable
Your XYZ must implement IComparable or (better yet) IComparable<XYZ<T, U>>.
If that's not possible, you must either create a class that implements IComparer or (again, better yet) IComparer<XYZ<String, String>> and use the overload of Sort() that takes an instance of such an object, or else use the form that takes a delegate or lamda expression that matches Comparison<XYZ<String, String>> (taking two XZY<String, String> arguments, and returning int).
If the Sort() method can't work out which XYZ comes before which, then it can't sort them.
I resolved the issue using linq statement.
rather than using PassedList.Sort(), I used the following statement:
PassedList = PassedList.OrderBy( x => x.Column1).ToList();
When I have SortedDictionary<TK, TV> in .NET and I want to enumerate it as ICollection<KeyValuePair<TK, TV>> does it enumerate in expected order?
That is KeyValuePair<TK, TV> with lowest key is returned as first, folloved by KeyValuePair<TK, TV> with second lowest key etc.?
Note: Only answer backed up by reference will be accepted.
From the Reference for GetEnumerator:
"The dictionary is maintained in a sorted order using an internal tree. Every new element is positioned at the correct sort position, and the tree is adjusted to maintain the sort order whenever an element is removed. While enumerating, the sort order is maintained."
Specifically: "While enumerating, the sort order is maintained."
Yes definitely, although you are going to find it very hard to find documentation that clarifies this precisely.
Although the documentation for each of the four GetEnumerator overloads on this type make vague statements about returning "an enumerator that iterates through a collection", it is obvious enough that they should produce equivalent (sorted by key) sequences; remember that a sorted-dictionary is meant to "represent a collection of key/value pairs that are sorted on the key." It would be highly unintuitive and confusing for users if a collection behaved completely differently (i.e. with a different enumeration order) between a foreach loop and a LINQ to Objects query, for example.
The best I can do is provide you with the implementations of the two GetEnumerator methods you appear to be interested in (as of .NET 4.0). They are identical - they return an instance of the nested Enumerator type, with the same arguments for its constructor. The only difference is the boxing of the struct-type in the second overload:
// Used when you do foreach(var kvp in dict) { ... }
public Enumerator<TKey, TValue> GetEnumerator()
{
return new Enumerator<TKey, TValue>
((SortedDictionary<TKey, TValue>) this, 1);
}
// Used when you do:
// foreach(var kvp in (ICollection<KeyValuePair<TKey, TValue>>)dict) { ... }
// or use LINQ to Objects on the collection.
IEnumerator<KeyValuePair<TKey, TValue>>
IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return new Enumerator<TKey, TValue>
((SortedDictionary<TKey, TValue>) this, 1);
}
In fact, the only GetEnumerator overload that has a slightly different implementation is the IDictionary.GetEnumerator method. This changes an argument to the constructor-call such that the resulting enumerator produces DictionaryEntry instances rather than KeyValuePair<,> instances. Of course, the enumeration order will still be the same as with the other overloads.
It depends on the default IComparer implementation of the key, assuming you are not passing one in:
SortedDictionary(Of TKey, TValue) requires a comparer implementation to perform key comparisons. You can specify an implementation of the IComparer(Of T) generic interface by using a constructor that accepts a comparer parameter; if you do not specify an implementation, the default generic comparer Comparer(Of T).Default is used. If type TKey implements the System.IComparable(Of T) generic interface, the default comparer uses that implementation.
Look at the Remarks section of the SortedDictionary<TKey, TValue> page.
So, if your key is a string, the string implementation of IComparable will be used, if an int32 the int32 implementation will be used.