kuskmen
kuskmen

Reputation: 3775

Implementing IConvertible interface

I have custom type named LocalizedString and I need to implement IConvertible interface since when we serialize types to our DB we use Convert.ChangeType and I can't change this part of the code.

What I did is I implemented:

string IConvertible.ToString(IFormatProvider provider)
{
    return string.Format(this.ToString());
}

object IConvertible.ToType(Type conversionType, IFormatProvider provider)
{
    return Convert.ChangeType(this.ToString(), conversionType);
}

public TypeCode GetTypeCode()
{
    return TypeCode.Object;
}

and I left all other methods with NotImplementedException since I dont have any reasonable conversions to other types. (I will eventually make them throw InvalidCastException , but thats another story.)

However I still receive when I invoke Convert.ChangeType(val, pr.__property.PropertyType) where val = "" , pr.__property.PropertyType = {Name = "LocalizedString" FullName = "Sampo.CMS.LocalizedString"} code crashes with:

Invalid cast from 'System.String' to 'Sampo.CMS.LocalizedString'.

What do I need to do more? I am stuck.

Upvotes: 0

Views: 24138

Answers (2)

Prince Owen
Prince Owen

Reputation: 1505

A full implementation of IConvertible based on Brent Rittenhouse's answer.

public struct TableAddress : IConvertible 
{
        /*....*/
    
        #region IConvertible Implementation
        static T ThrowNotSupported<T>()
        {
            var ex = ThrowNotSupported(typeof(T));
            return (T)ex;
        }

        static object ThrowNotSupported(Type type)
        {
            throw new InvalidCastException($"Converting type \"{typeof(TableAddress)}\" to type \"{type}\" is not supported.");
        }

        TypeCode IConvertible.GetTypeCode()
        {
            return TypeCode.Object;
        }

        bool IConvertible.ToBoolean(IFormatProvider provider) => ThrowNotSupported<bool>();
        char IConvertible.ToChar(IFormatProvider provider) => ThrowNotSupported<char>();
        sbyte IConvertible.ToSByte(IFormatProvider provider) => ThrowNotSupported<sbyte>();
        byte IConvertible.ToByte(IFormatProvider provider) => ThrowNotSupported<byte>();
        short IConvertible.ToInt16(IFormatProvider provider) => ThrowNotSupported<short>();
        ushort IConvertible.ToUInt16(IFormatProvider provider) => ThrowNotSupported<ushort>();
        int IConvertible.ToInt32(IFormatProvider provider) => ThrowNotSupported<int>();
        uint IConvertible.ToUInt32(IFormatProvider provider) => ThrowNotSupported<uint>();
        long IConvertible.ToInt64(IFormatProvider provider) => ThrowNotSupported<long>();
        ulong IConvertible.ToUInt64(IFormatProvider provider) => ThrowNotSupported<ulong>();
        float IConvertible.ToSingle(IFormatProvider provider) => ThrowNotSupported<float>();
        double IConvertible.ToDouble(IFormatProvider provider) => ThrowNotSupported<double>();
        decimal IConvertible.ToDecimal(IFormatProvider provider) => ThrowNotSupported<decimal>();
        DateTime IConvertible.ToDateTime(IFormatProvider provider) => ThrowNotSupported<DateTime>();
        string IConvertible.ToString(IFormatProvider provider) => ThrowNotSupported<string>();

        object IConvertible.ToType(Type conversionType, IFormatProvider provider)
        {
            if (conversionType == typeof(TableAddress))
            {
                return this;
            }
            
            // Other implementations here

            return ThrowNotSupported(conversionType);
        }
        #endregion 
            
        /*....*/
}

Upvotes: 3

Brent Rittenhouse
Brent Rittenhouse

Reputation: 857

If you were looking at the MSDN System.IConvertable example you'd see they used Convert.ChangeType in their implementation but that's because they are passing in a double to be converted. Convert.ChangeType is not going to have any idea how to convert to or from your custom object type. I'm pretty sure you need to implement the conversion inside that such as:

object IConvertible.ToType(Type conversionType, IFormatProvider provider)
{
    if (conversionType == typeof(Sampo.CMS.LocalizedString))
    {
        // Do your conversion here and return the string.
        return this.ToString() + "!!!!";
    }

    throw new InvalidCastException($"Converting type \"{typeof(LocalizedString )}\" to type \"{conversionType.Name}\" is not supported.");
}

Really, for the rest of the 16 or so methods you have to implement you could probably just throw InvalidCastExceptions for them as well...

Also, is there a reason you're doing the string.Format(this.ToString()) in the IConvertable.ToString implementation? You aren't passing it any parameters so shouldn't it just be return this.ToString()?


Here are some possibly helpful links:

Type conversion example in C# .NET using the IConvertible interface

System.Convert source from some version. It has the source for Convert.ChangeType so you can take a look and see what it's actually doing and why that won't work.

Upvotes: 2

Related Questions