Tuesday, August 22, 2006

Hiding generics complexity

On my last post I showed you some wicked use of generics, Ayende says he can't read code like that and provided an alternative; I agree with him, it is very ugly code, but there is hope!

here's the equivalente code for the first section (note that I'm still using generics, even if it doesn't look like it), where we wanted to store a list of file names:

FilesList filesList = GetFilesList();
ListFiles(filesList);
Then we have the bidimensional generic, to store folder name and the files in it:
FoldersWithFiles foldersWithFiles = GetFoldersWithFiles();
foreach (FoldersWithFilesPair kv in foldersWithFiles) {
Console.WriteLine(string.Format(" Folder: [{0}]", kv.Key));
ListFiles((FilesList)kv.Value);
}

And finally where we store computer names, with the folders, with the files in it:

ComputerFiles computerFiles = GetComputerFiles();
foreach (ComputersWithFoldersPair kv in computerFiles) {
Console.WriteLine(string.Format("Computer: [{0}]", kv.Key));
foreach (FoldersWithFilesPair kv1 in kv.Value) {
Console.WriteLine(string.Format(" Folder: [{0}]", kv1.Key));
ListFiles((FilesList)kv1.Value);
}
}
And my last example would now look like this:
ComputerFiles computerFiles = new ComputerFiles();
instead of this:
Dictionary<string, Dictionary<string, List<string>>> ComputerFiles = 
new Dictionary<string, Dictionary<string, List<string>>>();

Now, if the code is equivalente and I'm still using generics, what did I do?


I am using aliased generics (yup, invented here too J) which give me friendly names, instead of the ugly multidimensional generics syntax

Here's the aliases declaration:
using System.Collections.Generic;
using FoldersWithFilesPair = KeyValuePair<string, List<string>>;
using ComputersWithFoldersPair = KeyValuePair<string, Dictionary<string, List<string>>>;

using FilesList = List<string>;
using FoldersWithFiles = Dictionary<string, List<string>>;
using ComputerFiles = Dictionary<string, Dictionary<string, List<string>>>;

The best of both worlds, you can use complex generic data structures and keep your code readable.


You put all the uglyness in a simple place, and you are able to use friendly names which look like normal classes, plus you get more extensibility because you could change the signature of the aliased generics without breaking the code.


On my next post I'll show you another technique to accomplish the same thing, and some gotchas, then I'll put the full source code in a zip file so you can download it.

7 comments:

Ayende Rahien said...

That is better but I would still go with a custom type anyway.
This is likely to cause much head scratching when you are using it.
And it smells too much like a typedef to my liking

BlackTigerX said...

it's just a different, nicer way to approach generics and I like how it abstracts them

Anonymous said...

this is a great idea. top marks for thinking outside the angle brackets!

lb (secretGeek.net)

Anonymous said...

For what it's worth, it seems you can do this in .Net also, at least 2.0 you can. Me gusta mucho!

Alex James said...

I agree completely with Ayende. I am impressed with your use of aliases, but I still think a custom type is the way to go.

BlackTigerX said...

I guess this is one of those techniques where user preference plays an important role, there will be some who will love it and others who wouldn't use it, that's just how it is

Jan Wikholm said...

I did not know of this. (I found this from your own comment @ http://dev-tricks.net/c-sharp-using-alias-directives#more-218 )