views:

69

answers:

4

Imagine you've been given two System.Type's and you want to determine if there is an implicit or explicit type conversion from one to the other.

Without specifically checking for the static methods is there a built in method to determine that the type supports either or these conversions?

I know this is a brief body to a question but I think the scenario is relatively easy to explain, let me know if not.

Thanks in advance, Stephen.

A: 

You could try casting each one to the other and catching the exception

statichippo
Unfortunately I only have access to the Type object, not the actual objects themselves, so I wouldn't be able to do this.
meandmycode
A: 

I think Type.IsAssignableFrom should give you what you need.

[edit] note that this does NOT consider conversion operators, so it's possible that this is not useful to you. Worth mentioning anyway.

tenfour
That won't take any custom conversion operators into consideration. It only looks at the type hierarchy.
driis
I'm not sure this handles the implicit/explicit type conversion operators that you can define with C#. For example if I write typeof(XName).IsAssignableFrom(typeof(string)) I get false. But XName does have an implicit type conversion from string.
meandmycode
+5  A: 

Expression.Convert can look for a user-defined conversion operator, but unfortunately it will just throw an exception if none is found. You could use it like this:

public static bool CanConvert(Type fromType, Type toType)
{
    try
    {
        // Throws an exception if there is no conversion from fromType to toType
        Expression.Convert(Expression.Parameter(fromType, null), toType);
        return true;
    }
    catch
    {
        return false;
    }
}
Quartermeister
Thanks Quartermeister this is definitely a solution!
meandmycode
@meandmycode: If you want to implement the same thing without the overhead of an exception, you can run Reflector on Expression.Convert to see exactly what it does. The interesting methods are HasIdentityPrimitiveOrNullableConversion, HasReferenceConversion, and GetUserDefinedCoercionMethod in System.Dynamic.Utils.TypeUtils.
Quartermeister
Nice, thanks for the research effort, I'm not completely against avoiding your suggested code, even if I implement the methods like TypeUtils does it may end up being more work to maintain that than it is to just catch the exception.
meandmycode
+2  A: 

I don't think so. You'll have use reflection and look for those good ol' op_Implicit and op_Explicit static methods on each type.

This brings up the very interesting question: which has a greater performance impact, reflection (this answer) or using exceptions for control flow (Quartermeister's)? I honestly couldn't guess. You might want to profile each and find out for yourself.

Dan Tao
Eep, perhaps this isn't such a bad thing, I was worried that looking for these specially named methods was fragile, but on the other hand I wouldn't ever expect these method names to change!
meandmycode
@meandmycode: Yeah, I'm not sure whether or not they're actually specified; but I think of them as being in the same category as `get_` and `set_` methods for properties -- *very* unlikely to change (though I share your discomfort with such an "under the hood" approach).
Dan Tao
I'm not 100% sure but close to :) I think you'll miss some build in conversions if you're only looking for these methods such as int to long and derived to base/interface. Shich would increase the fragility of the solution since other changes than the just changing the name of the method would then be a breaking change
Rune FS
@Rune FS: True -- I meant to suggest that these would have to be *included* in the OP's code, not that this could be treated as the complete solution.
Dan Tao
This solution does work, I briefly wrote it up, it doesn't include some of the built in primitive type conversions, but that isn't to hard to remedy, my solution so far looks something like this: return type.HasPrimitiveConversionTo(targetType) || type.HasImplicitConversionTo(targetType) || type.HasExplicitConversionTo(targetType);
meandmycode
Expression.Convert internally uses reflection to look for "op_Implicit" and "op_Explicit", so you pay the performance penalty for reflection in both cases.
Quartermeister
If you do it this way, don't forget to handle (or decide you don't want to handle): lifted conversions on nullable types, reference conversions to and from interfaces, and delegate variance.
Quartermeister
I'm not so much bothered about the performance of Expression.Convert's internals, just specifically that I'm waiting on a stack trace being generated.
meandmycode
In terms of my solution so far, I firstly attempt to do reflection myself to determine if a conversion exists, if those routes aren't fruitful I fall back to doing your solution. I still however need to benchmark to see if its worth avoiding the exception.
meandmycode
@Quartermeister: Yeah, I was actually going to add that as a little footnote (that you pay for reflection either way); but I wasn't sure if `Expression.Convert` might be optimized in such a way (e.g., dynamic compilation + caching) that it would beat the more straightforward method of calling `GetMethod` and `GetParameters` every time -- at least in terms of its use of reflection.
Dan Tao
@meandmycode: Are you using this in a high-performance scenario? If so then it's worth looking into the random comment I just made above to Quartermeister about dynamic compilation (essentially getting the `MethodInfo` for `op_Implicit`/`op_Explicit`, compiling a `Func<TIn, TOut>` delegate for it, and caching that delegate). Otherwise I would say: don't worry about the exception. I think the `try`/`catch` approach on an `Expression.Convert` statement is really quite good.
Dan Tao
Well, no I guess not, the thing this code creates needs high performance, but in terms of this code path, it will be called maybe 10-20 times in an apps lifetime. So it probably isn't worth worrying about the stacktrace overhead at all. I'd like to conclude with a thanks to both of you for the discussion about this, its been really useful :).
meandmycode