views:

655

answers:

4

I would like to implement conversion between two library classes by Convert.ChangeType in C#. I can change neither of the two types. For example converting between Guid and byte[].

Guid g = new Guid();
object o1 = g;
byte[] b = (byte[]) Convert.ChangeType(o1, typeof(byte[])); // throws exception

I am aware that Guid provides a ToByteArray() method, but I would like to have that called when Guid is converted to byte[]. The reason behind this is that the conversion also takes place in library code (AseDataAdapter) which I can not modify. So is it possible to define a conversion rule between two types without modifying the sourcecode of either of the two classes?

I was experimenting with TypeConverter, but doesn't seem to work either:

Guid g = new Guid();
TypeConverter tc = TypeDescriptor.GetConverter(typeof(Guid));
byte[] b2 = (byte[])tc.ConvertTo(g, typeof(byte[])); // throws exception

The variable tc gets set to System.ComponentModel.GuidConverter which doesn't support conversions to byte[]. Can I have two TypeConverters for the same class? Even if I could, wouldn't I need to prepend an attribute to the source code of the class to assign a TypeConverter?

Thanks

A: 

Unfortunately no you cannot - you could write an extension method that would appear to be a conversion between two types as part of the framework.

Andrew Hare
A: 

If the code that is performing the conversion supports TypeConverters you can use TypeConverterAttribute at an assembly level.

Richard
AFAIK, there is no assembly-level usage of TypeConverterAttribute; you can do per-type and per-property, and override either via TypeDescriptor - but no assembly level? Have I missed something?
Marc Gravell
TypeConverterAttribute is declared AttributeTargets.All... and ISTR seeing this used in WF.
Richard
A: 
System.ComponentModel.ICustomTypeDescriptor

Yes, it is possible. Read the documentation on MSDN for related information to 'inject' that into the running program. (TypeDescriptor provides the method IIRC).

leppie
That is massively overkill... either use [TypeConverter] on an individual property (respected by PropertyDescriptor), or use the global approach shown in my post.
Marc Gravell
+8  A: 

You can change the registered TypeConverter for something using TypeDescriptor.AddAttributes; this isn't quite the same as Convert.ChangeType, but it may suffice:

using System;
using System.ComponentModel;
static class Program
{
    static void Main()
    {
        TypeDescriptor.AddAttributes(typeof(Guid), new TypeConverterAttribute(
            typeof(MyGuidConverter)));

        Guid guid = Guid.NewGuid();
        TypeConverter conv = TypeDescriptor.GetConverter(guid);
        byte[] data = (byte[])conv.ConvertTo(guid, typeof(byte[]));
        Guid newGuid = (Guid)conv.ConvertFrom(data);
    }
}

class MyGuidConverter : GuidConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(byte[]) || base.CanConvertFrom(context, sourceType);
    }
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return destinationType == typeof(byte[]) || base.CanConvertTo(context, destinationType);
    }
    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        if (value != null && value is byte[])
        {
            return new Guid((byte[])value);
        }
        return base.ConvertFrom(context, culture, value);
    }
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(byte[]))
        {
            return ((Guid)value).ToByteArray();
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}
Marc Gravell
Thanks for nice example :)
leppie
+1 Didn't even realise you could add attributes via TypeDescriptor.AddAttributes - that could be extremely useful.
Richard Szalay
Note that they aren't *real* attributes - reflection won't see them; only System.ComponentModel
Marc Gravell