Reputation: 3775
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
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
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