I recently made this mistake, which is really simple to make. I wanted to write some stuff out to XML using the new fancy IXmlSerializable interface. In order to convert my types to strings when saving and convert them back from string when loading, I used the TypeConverter class.
The particular example was with System.Drawing.Rectangle – I wanted to save out a rectangle and load it back. So I created a new TypeConverter() and called the ConvertToString(rect) method.
Everything looked fine in my XML file; I saw:
<tag attribute="{X=10,Y=12,Width=14,Height=16}"/>
The problem showed up when I tried to load the string – ConvertFromString was failing!
Here’s what happened. Because I created just a vanilla TypeConverter, and not the specific one for Rectangles (System.Drawing.RectangleConverter), TypeConverter.ConvertToString() just called Rectangle.ToString() – which puts it out in an {X=10, Y=12, Width=14, Height=16} format.
How do you correctly create a TypeConverter? Use TypeDescriptor.GetConverter() instead. This will look up the right converter for you.
Rectangle rect = new Rectangle(10, 12, 14, 16);
// Wrong
TypeConverter baseConverter = new TypeConverter();
string sample1 = baseConverter.ConvertToString(rect);
// Right
TypeConverter rectSpecificConverter = TypeDescriptor.GetConverter(rect);
string sample2 = rectSpecificConverter.ConvertToString(rect);
Running into this problem reminded me of yet another gotcha. Normally the way to hook up type converters to types is to use an attribute. If you look at the top of the Rectangle struct you can see the attribute:
[TypeConverter(typeof(RectangleConverter))]
public struct Rectangle {
But a class might change the TypeConverter by deriving from ICustomTypeDescriptor instead of specifying the TypeConverter attribute. In this case the TypeConverter is calculated at runtime - this means to get the correct converter you have to use the individual object, not the type. This changes the argument to GetConverter: prefer TypeDescriptor.GetConverter(someObject) over using TypeDescriptor.GetConverter(Type).
// Not ideal
TypeConverter rectSpecificConverter = TypeDescriptor.GetConverter(typeof(Rectangle));
// Ideal
TypeConverter rectSpecificConverter = TypeDescriptor.GetConverter(rect);
Here’s the full code, which outputs results to a RichTextBox.
No comments:
Post a Comment