PropertyInfo SetValue doesn't work for custom objects
I'm having problems setting a property through reflection when the type of the property isn't the same as the type of the object I'm setting it to, even though there is a conversion overload. To illustrate the problem, check out this sample code.
using System;
namespace TestCustomTypePropertySetValue
{
public class CustomThing
{
public CustomThing(string input)
{
m_input = input;
}public string Input
{
get { return m_input; }
}
private string m_input;public override string ToString()
{
return this.Input;
}public static implicit operator CustomThing(string input)
{
return new CustomThing(input);
}public static implicit operator string(CustomThing thing)
{
return thing.ToString();
}
}public class ClassWithThing
{
public CustomThing Thing
{
get { return m_thing; }
set { m_thing = value; }
}
private CustomThing m_thing = new CustomThing("default");
}class Class1
{
[STAThread]
static void Main(string[] args)
{
// set a custom thing to a string - this works
CustomThing thing = "test custom thing input";
// set a string to a custom thing - this works
string input = thing;// a property of type CustomThing
ClassWithThing cwt = new ClassWithThing();
System.Reflection.PropertyInfo propInfo =
cwt.GetType().GetProperty("Thing");// set the property to a string value - this fails
propInfo.SetValue(cwt, "test input", null);
}
}
}The last line in my sample code (PropertyInfo.SetValue) throws the exception "Object type cannot be converted to target type." If you run the sample code, you might notice that the SetValue doesn't even call the implicit conversion from string to CustomThing. It appears that SetValue short circuits the normal implicit conversion code.
I'm getting this exception in code where I don't know the type of the property that I'm calling SetValue() on, and I'd rather not have to hack in a check for my custom type. Does anyone have any ideas on how to do this?
Answers
Correct reflection will not use implicit conversion operators. In general they are frowned upon anyway (unlike C++ where they are commonly used). Implicit type conversion through operators is a C# thing so reflection (which is language independent) doesn't support it. However reflection does use the standard .NET conversion mechanism through TypeConverter. Unfortunately type converters aren't used with the default binding when assigning a value to a property. Therefore you'll have to convert the property value before you try to assign it using something along these lines:
TypeConverter conv = TypeDescriptor.GetConverter(propInfo.PropertyType);
if (conv.CanConvertFrom(typeof(string)))
{
object value = conv.ConvertFrom("test input");
propInfo.SetValue(cwt, value, null);
};Now you have to define a type converter for your custom class:
public class CustomThingTypeConverter : TypeConverter
{
public override bool CanConvertFrom ( ITypeDescriptorContext context, Type sourceType )
{
if (sourceType == typeof(string))
return true;return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom ( ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value )
{
if (value is string)
return new CustomThing(Convert.ToString(value));
return base.ConvertFrom(context, culture, value);
}public override object ConvertTo ( ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType )
{
if (destinationType == typeof(string))
return ((CustomThing)value).Input;return base.ConvertTo(context, culture, value, destinationType);
}
}Finally you have to associate the converter with the type:
[TypeConverter(typeof(CustomThingTypeConverter))]
public class CustomThing
{ ... }Do not confuse IConvertible with TypeConverter. IConvertible is used to conver from a custom type to other types, not vice versa. Finally note that implicit conversions are not common place in .NET unless you are dealing with reference types that act like value types, which is rare.
Michael Taylor - 11/2/06
All Replies
Correct reflection will not use implicit conversion operators. In general they are frowned upon anyway (unlike C++ where they are commonly used). Implicit type conversion through operators is a C# thing so reflection (which is language independent) doesn't support it. However reflection does use the standard .NET conversion mechanism through TypeConverter. Unfortunately type converters aren't used with the default binding when assigning a value to a property. Therefore you'll have to convert the property value before you try to assign it using something along these lines:
TypeConverter conv = TypeDescriptor.GetConverter(propInfo.PropertyType);
if (conv.CanConvertFrom(typeof(string)))
{
object value = conv.ConvertFrom("test input");
propInfo.SetValue(cwt, value, null);
};Now you have to define a type converter for your custom class:
public class CustomThingTypeConverter : TypeConverter
{
public override bool CanConvertFrom ( ITypeDescriptorContext context, Type sourceType )
{
if (sourceType == typeof(string))
return true;return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom ( ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value )
{
if (value is string)
return new CustomThing(Convert.ToString(value));
return base.ConvertFrom(context, culture, value);
}public override object ConvertTo ( ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType )
{
if (destinationType == typeof(string))
return ((CustomThing)value).Input;return base.ConvertTo(context, culture, value, destinationType);
}
}Finally you have to associate the converter with the type:
[TypeConverter(typeof(CustomThingTypeConverter))]
public class CustomThing
{ ... }Do not confuse IConvertible with TypeConverter. IConvertible is used to conver from a custom type to other types, not vice versa. Finally note that implicit conversions are not common place in .NET unless you are dealing with reference types that act like value types, which is rare.
Michael Taylor - 11/2/06

