Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I determine if an implicit cast exists in C#?

I have two types, T and U, and I want to know whether an implicit cast operator is defined from T to U.

I'm aware of the existence of IsAssignableFrom, and this is not what I'm looking for, as it doesn't deal with implicit casts.

A bit of googling led me to this solution, but in the author's own words this is ugly code (it tries to cast implicitly and returns false if there's an exception, true otherwise...)

It seems testing for the existence of an op_Implicit method with the correct signature won't work for primitive types.

Is there a cleaner way of determining the existence of an implicit cast operator?

like image 403
Brann Avatar asked Aug 15 '15 13:08

Brann


People also ask

Which of the following defines implicit casting?

In implicit typecasting, the conversion involves a smaller data type to the larger type size. For example, the byte datatype implicitly typecast into short, char, int, long, float, and double. The process of converting the lower data type to that of a higher data type is referred to as Widening.

What is example of implicit type conversion?

Implicit conversions For example, a variable of type long (64-bit integer) can store any value that an int (32-bit integer) can store. In the following example, the compiler implicitly converts the value of num on the right to a type long before assigning it to bigNum .

What are implicit data types?

Implicit Type Conversion is also known as 'automatic type conversion'. It is done by the compiler on its own, without any external trigger from the user. It generally takes place when in an expression more than one data type is present.

What are implicit conversions?

An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.


2 Answers

You could use reflection to find the implicit conversion method for the target type:

public static bool HasImplicitConversion(Type baseType, Type targetType) {     return baseType.GetMethods(BindingFlags.Public | BindingFlags.Static)         .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)         .Any(mi => {             ParameterInfo pi = mi.GetParameters().FirstOrDefault();             return pi != null && pi.ParameterType == baseType;         }); } 

You can use it like this:

class X {} class Y {     public static implicit operator X (Y y)     {         return new X();     }      public static implicit operator Y (X x)     {         return new Y();     } }  // and then: bool conversionExists = HasImplicitConversion(typeof(Y), typeof(X)); 

Note that this only checks for an implicit type conversion on the base type (the first passed type). Technically, the type conversion can also be defined on the other type, so you may need to call it again with the types reversed (or build that into the method). Implicit type conversions may not exist on both types though.

like image 58
poke Avatar answered Sep 22 '22 02:09

poke


I ended up handling the primitive types scenario manually. Not very elegant, but it works.

I've also added additional logic to handle nullable types and enums.

I reused Poke's code for the user-defined type scenario.

public class AvailableCastChecker {     public static bool CanCast(Type from, Type to)     {         if (from.IsAssignableFrom(to))         {             return true;         }         if (HasImplicitConversion(from, from, to)|| HasImplicitConversion(to, from, to))         {             return true;         }         List<Type> list;         if (ImplicitNumericConversions.TryGetValue(from, out list))         {             if (list.Contains(to))                 return true;         }          if (to.IsEnum)         {             return CanCast(from, Enum.GetUnderlyingType(to));         }         if (Nullable.GetUnderlyingType(to) != null)         {             return CanCast(from, Nullable.GetUnderlyingType(to));         }          return false;     }      // https://msdn.microsoft.com/en-us/library/y5b434w4.aspx     static Dictionary<Type,List<Type>> ImplicitNumericConversions = new Dictionary<Type, List<Type>>();      static AvailableCastChecker()     {         ImplicitNumericConversions.Add(typeof(sbyte), new List<Type> {typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(byte), new List<Type> { typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(short), new List<Type> {  typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(ushort), new List<Type> { typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(int), new List<Type> { typeof(long), typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(uint), new List<Type> { typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(long), new List<Type> { typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(char), new List<Type> { typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) });         ImplicitNumericConversions.Add(typeof(float), new List<Type> { typeof(double) });         ImplicitNumericConversions.Add(typeof(ulong), new List<Type> { typeof(float), typeof(double), typeof(decimal) });     }      static bool HasImplicitConversion(Type definedOn, Type baseType, Type targetType)     {         return definedOn.GetMethods(BindingFlags.Public | BindingFlags.Static)             .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType)             .Any(mi =>             {                 ParameterInfo pi = mi.GetParameters().FirstOrDefault();                 return pi != null && pi.ParameterType == baseType;             });      } } 
like image 36
Brann Avatar answered Sep 20 '22 02:09

Brann