views:

239

answers:

4

Given Type a and Type b, how can I, at runtime, determine whether there's an implicit conversion from a to b?

If that doesn't make sense, consider the following method:

public PropertyInfo GetCompatibleProperty<T>(object instance, string propertyName)
{
   var property = instance.GetType().GetProperty(propertyName);

   bool isCompatibleProperty = !property.PropertyType.IsAssignableFrom(typeof(T));
   if (!isCompatibleProperty) throw new Exception("OH NOES!!!");

   return property;   
}

And here's the calling code that I want to work:

// Since string.Length is an int property, and ints are convertible
// to double, this should work, but it doesn't. :-(
var property = GetCompatibleProperty<double>("someStringHere", "Length");
A: 

Take a look here for more info on implicit conversions.

Thomas Wanner
Didn't work. 15
Yuriy Faktorovich
Nope, doesn't work. See updated post.
Judah Himango
Int or Double is not what I meant by a custom type. Please read the linked question and answers and read my answer twice before judging it ;)
Thomas Wanner
@Thomas: I tried this with a custom type. I think he is talking about classes which can be converted not from inheritance but from user defined/.net defined implicit conversion.
Yuriy Faktorovich
Then it's an exact duplicate of the question I have linked ;)
Thomas Wanner
Thomas, you're right, the answer you link to has the proper answer (which is really like Jason's answer). The reason I discarded your answer was because you said IsASsignableFrom would work for custom types. That's not a true statement, I'm actually trying this on custom types, and it's failing.
Judah Himango
This answer is incorrect. For our purposes here, `IsAssignableFrom` only considers identity conversions, hierarchy conversions, and interface implementation conversions. It does not consider implicit conversions.
Jason
Ok, I don't want to go into some fights about wording, that was not the point. I edited the answer to at least keep a link to the dupe.
Thomas Wanner
Ok, thanks Thomas. I think the confusion came in because you were thinking of non-user-defined conversions. My question is about implicit conversions defined in my classes.
Judah Himango
+2  A: 

Note that IsAssignableFrom does NOT solve your problem. You have to use Reflection like so. Note the explicit need to handle the primitive types; these lists are per §6.1.2 (Implicit numeric conversions) of the specification.

static class TypeExtensions { 
    static Dictionary<Type, List<Type>> dict = new Dictionary<Type, List<Type>>() {
        { typeof(decimal), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } },
        { typeof(double), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
        { typeof(float), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } },
        { typeof(ulong), new List<Type> { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } },
        { typeof(long), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } },
        { typeof(uint), new List<Type> { typeof(byte), typeof(ushort), typeof(char) } },
        { typeof(int), new List<Type> { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } },
        { typeof(ushort), new List<Type> { typeof(byte), typeof(char) } },
        { typeof(short), new List<Type> { typeof(byte) } }
    };
    public static bool IsCastableTo(this Type from, Type to) { 
        if (to.IsAssignableFrom(from)) { 
            return true; 
        }
        if (dict.ContainsKey(to) && dict[to].Contains(from)) {
            return true;
        }
        bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static) 
                        .Any( 
                            m => m.ReturnType == to &&  
                            m.Name == "op_Implicit" ||  
                            m.Name == "op_Explicit" 
                        ); 
        return castable; 
    } 
} 

Usage:

bool b = typeof(A).IsCastableTo(typeof(B));
Jason
This will tell us that it has an implicit or explicit conversion method. Not whether it can be implicitly converted between specific types.
Sam
That's ok, Sam, this will work for my issue. I'm a little surprised there's no built-in way to do this.
Judah Himango
Why not use the enumerable Any extension?
Yuriy Faktorovich
@Yuriy Faktorovich: Updated.
280Z28
@Jason: You should name it `IsCastableFrom` and reverse the arguments to match the naming of similar framework methods.
280Z28
@280Z28: Thanks.
Jason
@280Z28: You're right; in hindsight `IsCastableFrom` would have been better.
Jason
@Sam: What do you mean?
Jason
A: 

Don't reply on implicit conversion. Use TypeConverter.CanConvertTo to check if something can be converted and TypeConverter.ConvertTo to perform the conversion.

Sam
That throws a NotSupportedException in my scenario.
Judah Himango
+1  A: 

Implicit conversions you'll need to consider:

  • Identity
  • sbyte to short, int, long, float, double, or decimal
  • byte to short, ushort, int, uint, long, ulong, float, double, or decimal
  • short to int, long, float, double, or decimal
  • ushort to int, uint, long, ulong, float, double, or decimal
  • int to long, float, double, or decimal
  • uint to long, ulong, float, double, or decimal
  • long to float, double, or decimal
  • ulong to float, double, or decimal
  • char to ushort, int, uint, long, ulong, float, double, or decimal
  • float to double
  • Nullable type conversion
  • Reference type to object
  • Derived class to base class
  • Class to implemented interface
  • Interface to base interface
  • Array to array when arrays have the same number of dimensions, there is an implicit conversion from the source element type to the destination element type and the source element type and the destination element type are reference types
  • Array type to System.Array
  • Array type to IList<> and its base interfaces
  • Delegate type to System.Delegate
  • Boxing conversion
  • Enum type to System.Enum
  • User defined conversion (op_implicit)

I assume you're looking for the latter. You'll need to write something resembling a compiler to cover all of them. Notable is that System.Linq.Expressions.Expression didn't attempt this feat.

Hans Passant
Heh. Interesting. I'm really surprised there's no baked-in way to say, "This type can be converted to this other type".
Judah Himango
"Array to array when arrays are same length and element has implicit conversion" Are you sure? I don't think so. In fact, I don't think there's an explicit conversion. As for the rest, I think my method covers them all. Thus, I must be misunderstanding what you mean by "you'll need to write something resembling a compiler to cover all of them."
Jason
Yeah, I'm sure. Derived[] is implicitly convertible to Base[].
Hans Passant
Okay yes, I agree but that's slightly different than your initial statement. There is an implicit conversion from `int` to `double` but there is not an implicit conversion from `int[]` to `double[]`.
Jason
Well, of course. The exact rulez are spelled out in the language spec, chapter 6.1
Hans Passant