C# Generics Annoyance
Let my start by saying this is not a huge Annoyance. This is more of a “winy girl who didn’t get to go to Miami” annoyance. There is a perfectly good solution to this, it just isn’t the solution with the syntax that I want. That, and I feel like wining. The crux of the issue is that I can’t write a generic property the way I can write a generic method.
Anyway, I’ve been writing a lot of data bound web sites these days. As such, you end up having to resolve the same problems over and over and over again. One such problem is how to load a drop down list control generically, and simply. This is not a complicated control, but it is still possible to make mistakes.
One of the other reasons this comes up a lot is because I use the MVP pattern when creating my web applications. Something I’ve found when writing my view: don’t care about what type the data is if you don’t have to. I think this is related to something called “duck typing”, but I’m too lazy to look it up and make sure right now. Pretty sure it is a Ruby thing — but those guys are just crazy anyway.
A prime example of this has to do with the drop down list. I can pass it a DataSet, an IEnumerable, or a generic list of whatever data I want. It has these two wonderful properties: DataTextField and DataValueField that determine what is displayed and what is returned by the control. It works great — but this should be a great example of not having to care about what I’m passing it. The presenter needs to care, but the view should give two hoots what goes in there.
So to help solve this problem I created a new generic class called NameValue. The basics of the control are this: you can define a Name and a Value. You can also send it a list of a single value, if you want, but you still get the Name/Value pair their to play with.
If you are still in the dark ages (like me most of the time) and still writing your code with C# 2.0 (.Net 2.), the class will look like this:
[Serializable] public class NameValue<N,V> { private N _name; private V _value; public NameValue() { } public NameValue(N name, V value) { _name = name; _value = value; } public N Name { get { return _name; } set { _name = value; } } public V Value { get { return _value; } set { _value = value; } } }
If you are using C# 3.0 (and thereby forcing me to hate you), the class will look like this:
[Serializable] public class NameValue<N,V> { public N Name {get;set; } public V Value { get; set; }
public NameValue() { } public NameValue(N name, V value) { Name = name; Value = value; } }
The next step, in the presenter, is to load a Generic list with a bunch of data in this class. For argument sake we will say it looks like this:
public List<NameValue<string, string>> GetListOfData() { List<NameValue<string, string>> list =
new List<NameValue<string, string>>(); list.Add(new NameValue<string, string>("Hi there", "1")); list.Add(new NameValue<string, string>("Hi there2", "2")); list.Add(new NameValue<string, string>("Hi there3", "3")); list.Add(new NameValue<string, string>("Hi there4", "4")); return list; }
Again, if you are using C# 3.0, and I hate you for that, you could change the first line of code to look like this:
var list = new List<NameValue<string, string>>();
Then you can set the properties by name like this:
list.Add(new NameValue<string, string> { Name = "Hi there-1", Value = "1" });
But really, is it really that better? (Actually, I think it is. But I’m not going to say that until I’m using it full time. Until then, all of you using C# 3.0 still suck).
OK, now here comes the really whiny part. I want to hand this data to a drop down list (a combo box for windows developers). All the drop down list needs to know is there there is a name and a value in the object. It can even know that it is being handed a generic list of NameValue objects. I just don’t want to have to specify what the types of the name and value are.
Normally when I’m handing data to a view, I give the data through properties. So, for my previous example I could code a property into my view interface like this:
List<NameValue<string, string>> ComboBoxData { set; }
But there is that <string, string> part in there. What if I have to change the date type to an int and a double at the last minute? (not freaking likely, but I’ve had to change a double to a decimal WAY to many times to make an annoyingly type specific database happy at this point in my life — and yes, that was a run on sentence. And I did it on purpose to make just to see your face turn blue).
Here is the rub, I can declare a method that does just what I want it to, and it works perfectly. Here is is:
void LoadComboBox<N, V>(List<NameValue<N, V>> list);
Now I can pass the data to the view and the view will be blissfully unaware what is happening to it. Just like a good alien abduction.
Now I thoroughly believe both of my readers are way smarter than me and are only reading my blog out of pity for me (instead of pity, send money. No, really). But if you are wondering how I am loading the generic data into the drop down list and still keeping it generic, here is another method for you:
protected void LoadListControl<N,V>(
ListControl ctrl,
List<NameValue<N, V>> list) { ctrl.DataTextField = "Name"; ctrl.DataValueField = "Value"; ctrl.DataSource = list; ctrl.DataBind(); }
Very simply, this will take any generic list of NameValue objects and assign it to any control that inherits from ListControl (like a drop down list). As a bonus, I can now assign the data coming in from the presenter to the drop down list in one line of code:
Now my ComboBoxData property can look like this (assume SampleDropDownList is a DropDownList control):
public List<NameValue<string, string>> ComboBoxData { set { LoadListControl(SampleDropDownList, value); } }
Of course, if you are a real slime-ball, and are using C# 3.0 before God intended (meaning: before I get to), you could change the LoadListControl method defined above and turn it into an extension method that extends the ListControl itself. You would need to put this code in a static class, reference the classes namespace, and add the eye of newt to work.
public static void LoadNameValue<N, V>(this ListControl ctrl,
List<NameValue<N, V>> list ) { ctrl.DataTextField = "Name"; ctrl.DataValueField = "Value"; ctrl.DataSource = list; ctrl.DataBind(); }
Now we can rewrite the view load routine to a simpler piece of code that is easier to move from form to form. It looks like this (assume SampleDropDownList is a DropDownList control):
public void LoadComboBox<N, V>(List<NameValue<N, V>> list) { SampleDropDownList.LoadNameValue(list); }
So what do we have now? We have the ability to load a DropDownList, from a presenter, using true name/value data pair, and only the presenter knows what the data really is. If I could do that through a property, and not a method, I would be happier, but I will take this as a close second.
As for the rest of the data on my view, I’m stuck with sending data in strings, ints, doubles, DateTime, and making liberal use of IEnumerable (for setting data in a GridView).
Panacea this ain’t; and it isn’t like we are using a dynamic language. But this does give a glimpse of both worlds. I stick with my previous statement. If the view doesn’t need to care what the data type is, then it shouldn’t be forced to. Of course, the presenter should always care. It is the gate keeper that makes sure the data is correct.
And for all of you who are using C# 3.0 before I get to, whose current client laughs in face of new technology and says “bring it on”, and show no fear about tip-toeing on the bleeding edge: Extension Methods are AUSOME BABY. LINQ and Lambda are really cool, don’t me wrong, very powerful. But day in day out what are you going to use the most? Extension Methods. You are going to get a lot of mileage out of these things.
Don’t forget to use the Collection Initializers for C# 3.0:
var list = new List { “String1”, “String2”, “String3” };
That should have been List<string>
Hey Chris,
First off, Extension Methods are the Devil, so don’t envy those 3.5 mofos too much.
Second, your code would be much more terse if you used a NameValueCollection rather than List<NameValue>.
Ah but you say its IEnumerable implementation is flawed. Quite true, but you could write a simple implementation in less than 100 lines of code. Or you could ask to see my implementation. Plus this will work for those stone age development environments like .Net 1.1.
-Bill
Steve: collection initializers are very nice, and I didn’t completely forget about them. If you include the Collection initializers with object initializers you could actually remove the constructors from the NameValue object all together. And I thought of doing just that. I decided against it when I saw all the extra code you would have to write to make it work.
But I will probably have to modify the post to show collection initializers now.
Hi Bill,
1. I read that article of yours:
http://blechie.com/WPierce/archive/2008/01/04/Extension-Methods-are-the-Devil.aspx
But I still think extension methods are an elegant way to extend 3rd party apis (like the entire .Net framework) to better suite your needs. The other option is that I could create a custom DropDownList that inherits from DropDownList for the sole purpose of adding the method to it. The extension method is far simpler to implement.
2. I agree it could have been more terse. But I don’t always think terse is good. I will take readability over terse any days. But NameValueCollection is very old school (and only accepts strings — which I’m against), a better option would be the Dictionary object which is more type safe. The downside of the Dictionary object is that the list is indexed by the key, and that isn’t what I always want.
3. I left .Net 1.1 a while back, I don’t even have Visual Studio 2003 installed anymore. Also, I know that FxCop doesn’t think generics should be in a public api. This is a case where I will just disagree with FxCop — especially since my api’s sole purpose in life is to feed data to a web site. It isn’t a very broad range for an api. If I was designing an api for something like SubSonic, I would reconsider that argument, but I still don’t think I would taylor my api to .Net 1.1 anymore.
But, if you have another solution, by all means let me see it. Please. My email is [email protected]
I think that FxCop only likes you to use the Collection generic, since the other generic classes won’t pass the events on properly, or something like that.
But FxCop complains about a lot of stuff.