EnsureTypeAssignable suggestions

Mar 21, 2010 at 7:45 AM

My program were throwing exceptions on serialization of Dictionary objects with custom keys. After searching through these discussions and the source code, I found out that the serializer will work if the key class implements IConvertible.

However deserialization still do not work. Since IConvertible does not work in the reverse direction, I added a new cast operator to my class:

        public static implicit operator Key(string value)
	    {
            return Parse(value);
	    }

Still, I'm unable to get the deserializer working.

The only other option is adding a TypeConverter attribute, but I'd prefer a simpler solution.

Thus can we do any of the following:

  1. Add the following code snippet
    var cast = targetType.GetMethod("op_Implicit", new[] {initialType});
    if(cast != null)
      return cast.Invoke(value, new[] { });
    
    
  2. or, ... add a converter that will instantiate objects from a string
  3. or, ... add a new Attribute which will define factory methods
Coordinator
Mar 21, 2010 at 10:52 AM

Using the implicit/explicit cast method intrigues me. I'll look into it. For now the way to do it is to use a TypeConverter.

Mar 22, 2010 at 11:00 AM
Edited Mar 22, 2010 at 11:01 AM

It should not be too difficult to integrate. The place that makes most sense is the following method:

 

    private static object EnsureTypeAssignable(object value, Type initialType, Type targetType)
    {
      Type valueType = (value != null) ? value.GetType() : null;

      if (value != null && targetType.IsAssignableFrom(valueType))
        return value;
      else if (value == null && ReflectionUtils.IsNullable(targetType))
        return null;
      else
        throw new Exception("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != null) ? initialType.ToString() : "{null}", targetType));
    }

 

Thus it becomes:

    private static object EnsureTypeAssignable(object value, Type initialType, Type targetType)
    {
      Type valueType = (value != null) ? value.GetType() : null;

      if (value != null && targetType.IsAssignableFrom(valueType))
        return value;
      else if (value == null && ReflectionUtils.IsNullable(targetType))
        return null;
      
      var cast = targetType.GetMethod("op_Implicit", new[] {initialType});
      if(cast != null)
	  return cast.Invoke(value, new[] { });
	      
      throw new Exception("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != null) ? initialType.ToString() : "{null}", targetType));
    }

I have not tested the code (even whether it compiles), but it should probably be a good starting point.

Update: The conversion operation is static. Thus GetMethod should have a thid parameter for this, and cast.Invoke should be Invoke(null, new [] { value });

 

Coordinator
Mar 23, 2010 at 2:13 AM

Done. I implemented it a different way (dynamic code generation + caching for performance) but the end result is the same.

http://json.codeplex.com/SourceControl/list/changesets

Mar 23, 2010 at 8:53 AM

Thanks that was fast.

Does it work for static methods? Mine looks like the following:

.method public hidebysig specialname static class Key op_Implicit(string 'value') cil managed

Mar 23, 2010 at 8:56 AM

I found this on StackOverflow:

http://stackoverflow.com/questions/2075471/implicit-version-of-isassignablefrom

 

public static class TypeExtentions
{
    public static bool ImplicitlyConvertsTo(this Type type, Type destinationType)
    {

        if (type == destinationType)
            return true;


        return (from method in type.GetMethods(BindingFlags.Static |
                                               BindingFlags.Public)
                where method.Name == "op_Implicit" &&
                      method.ReturnType == destinationType
                select method
                ).Count() > 0;
    }
}

They also seem to be checking the return type.

There was another variant verifying that the method is "specialname"