Multi-value Dictionary C# source code (.NET 3.5)

By popular demand, I've published the C# source code of my Multi-value Dictionary class, which can also merge dictionaries into itself and which implements ILookup<T, V> as well. It's part of Algorithmia, our upcoming data-structure and algorithm library which will ship with LLBLGen Pro v3.0 later this year. The code is released under the BSD2 license, see the enclosed readme.txt. The class comes with its own general purpose Grouping<T, V> class as well and of course its own ToMultiValueDictionary() extension method.

I hope this is useful to others.

Update: it seems that if you run a Linq query (Linq to objects) over the MultiValueDictionary, the compiler and intellisense get confused as there are now two enumerators and both work with the linq operators, which means you either want to remove the ILookup code from the class (which is not that hard) or explicitly state the generic arguments. It's not a big problem, though in case you run into this problem, you know the reason.

Update 2:A user pointed out that I forgot to include ArgumentVerifier, a simple class to make life easier wrt verifying arguments, so I've included that one as well.

8 Comments

  • >> the compiler and intellisense get confused

    Perhaps you need to rethink the design? :-)

  • @Steven: I have no choice: the class inherits from Dictionary (implements IEnumerable) and ILookup also implements IEnumerable but different generic types. So ILookup has to change, but that's not my call (it's a BCL interface ;)).

  • You can make the ILookup implementation explicit. That way, if anyone wants the ILookup functions, they can cast to ILookup (and thus get the appropriate extension methods).

    Otherwise, the default behavior would be that intellisense will show only fields (and extensions) from non-explicitly implemented interfaces.

  • @Mark: I made it explicit, that's the point: a Linq to objects enumerable method like Where also works with ILookup.IEnumerable<IGrouping>, which makes it problematic because there are now two enumerables and which one to choose by the compiler? so the compiler wants to explicitly define the generic parameters on the linq extension methods, which is awkward.

  • Wow. You're right. I just brewed up a test class that implements IEnumerable and explicitly implements ILookup.

    While intellisense correctly shows the overloads for the extension methods of associated with the IEnumerable interface, it appears that the parser/compiler gets confused about how to interpret the various extension methods that are applicable to the explicitly defined ILookup interface.

    I had (incorrectly) assumed that Microsoft made the parser/compiler mirror the functionality of the intellisense drop down. That seems like a design flaw to me...

    So, yeah, I guess the only thing left is something like this: add a property called "Values" (or whatever seems appropriate) that returns an IEnumerable, which iterates over the same enumeration that the IEnumerable implementation that the dictionary's IEnumerable implementation iterates over. That could be implemented as a simple redirection, but it would make both the compiler and intellisense happy.

    That way the user can do something like this:

    var dictionary = GetMultiValueDictionary();
    dictionary.Values.Select( t=> t.Length ).Where( t=> t > 0 );

    This could even be implemented externally as an extension method instead of as a property on the original class:

    var dictionary = GetMultiValueDictionary();
    dictionary.GetValues().Select( t=> t.Length ).Where( t=> t > 0 );

  • @Mark: I think the compiler simply checks whether a type implements IEnumerable and as the type implements that interface twice (once as the Dictionary and once as ILookup), it gets confused. As ILookup seems to be some kind of specific linq query interface, I think the scope for the interface is smaller than the one it is used in with this class.

    the GetValues is indeed a 'workaround', though thinking about ILookup some more, I think it shouldn't be on this dictionary, it doesn't really add anything.

  • a newbie and stupid question, but why do I need it?
    e.g. As I understand it is for storing values with 2 different keys.
    How would I connect it to a relational world, e.g. what to store in such a collection? Sometimes your articles are hard to read for not-godlike coders like you ;p

  • @Steve: in the relational world, it can be very handy :) How about you have a list of Order objects, and you want to store all orders under their customerid FK field value? With this dictionary you can do that: the customerid field is the key, the values are the order instances, which allows you to quickly find the Order instances for a given customerID in memory using the dictionary without needing to traverse all orders sequentially

Comments have been disabled for this content.