How to check if a type can be converted to another type in C #

I have two types of sourceType and targetType , and I need to write a method in C # that checks if sourceType values โ€‹โ€‹can be assigned to targetType variable. Signature of the MatchResultTypeAndExpectedType(Type sourceType, Type targetType) .

Inheritance is covered by IsAssignableFrom . In the case of convertible types, I thought of using CanConvertFrom , but, for example, if both types are numeric, then it always returns false . The test that I performed:

 TypeConverter typeConverter = TypeDescriptor.GetConverter(typeof(Decimal)); Console.WriteLine("Int16 to Decimal - " + typeConverter.CanConvertFrom(typeof(Int16))); Console.WriteLine("UInt16 to Decimal - " + typeConverter.CanConvertFrom(typeof(UInt16))); typeConverter = TypeDescriptor.GetConverter(typeof(long)); Console.WriteLine("UInt16 to Int64 - " + typeConverter.CanConvertFrom(typeof(uint))); typeConverter = TypeDescriptor.GetConverter(typeof(Double)); Console.WriteLine("UInt16 to Double - " + typeConverter.CanConvertFrom(typeof(UInt16))); typeConverter = TypeDescriptor.GetConverter(typeof(String)); Console.WriteLine("UInt16 to String - " + typeConverter.CanConvertFrom(typeof(UInt16))); 

Result:

 Int16 to Decimal - False UInt16 to Decimal - False UInt16 to Int64 - False UInt16 to Double - False UInt16 to String - False 

[EDIT] So, my question is: Is there a way in .NET to check if a value of a given type can be assigned to a variable of another type without knowing the values, for example, will implicit conversion succeed? More specific requirements for implementing MatchResultTypeAndExpectedType(Type sourceType, Type targetType) :

  • The sources and types of targets are not known at compile time, since their assemblies are loaded later.
  • Values โ€‹โ€‹or objects cannot be specified as input.
  • Type objects cannot be created in the implementation, since they are not allowed by the rest of the system. Values โ€‹โ€‹of value types can be created.
  • You need to check only the implicit conversion.

It is known whether sourceType a value type. Thus, the method signature can be as MatchResultTypeAndExpectedType(Type sourceType, Boolean isSourceValueType, Type targetType)

One way is to implement the Implicit Numeric Conversion Table , but it will not cover other conversions or user-defined conversions.

+4
source share
2 answers

The problem with implicit / explicit conversions is that they are solved at compile time. So (as far as I know) there is no simple runtime check. However, the dynamic implementation will select them and call them at runtime. You can (as if ugly) create a class that will try to perform the conversion, catch an exception if it does not work, and tell whether it passed it or not:

 public class TypeConverterChecker<TFrom, TTo> { public bool CanConvert { get; private set; } public TypeConverterChecker(TFrom from) { try { TTo to = (TTo)(dynamic)from; CanConvert = true; } catch { CanConvert = false; } } } 

For some classes, such as:

 public class Foo { public static implicit operator Bar(Foo foo) { return new Bar(); } public static implicit operator Foo(Bar bar) { return new Foo(); } } public class Bar { } public class Nope { } 

Using:

 Console.WriteLine((new TypeConverterChecker<Foo, Bar>(new Foo())).CanConvert); //True Console.WriteLine((new TypeConverterChecker<Bar, Foo>(new Bar())).CanConvert); //True Console.WriteLine((new TypeConverterChecker<Foo, Nope>(new Foo())).CanConvert); //False 

And with the types you checked:

 Console.WriteLine((new TypeConverterChecker<Int16, Decimal>(0)).CanConvert); //True Console.WriteLine((new TypeConverterChecker<UInt16, Decimal>(0)).CanConvert); //True Console.WriteLine((new TypeConverterChecker<UInt16, Int64>(0)).CanConvert); //True Console.WriteLine((new TypeConverterChecker<UInt16, Double>(0)).CanConvert); //True Console.WriteLine((new TypeConverterChecker<UInt16, String>(0)).CanConvert); //False 

Now I can imagine that this can be changed to be more efficient (cache the result statically, so subsequent requests for the same combination TFrom, TTo should not try to convert, because value types ignore the need for an input instance (just use default(TFrom) ), etc., but it should give you a TFrom from should be noted that you should not pass null for TFrom from , since all null will pass (unless it is a value type)

You can also add a second try/catch to try using the Convert.ChangeType method and see if the types are defined by IConvertable implementations that can be used. (you can save this as a separate boolean flag so that you know what type of conversion you need to perform later)

EDIT: If you don't know the types at compile time, you can use a little reflection to still use the conversion checker:

 public static class TypeConverterChecker { public static bool Check(Type fromType, Type toType, object fromObject) { Type converterType = typeof(TypeConverterChecker<,>).MakeGenericType(fromType, toType); object instance = Activator.CreateInstance(converterType, fromObject); return (bool)converterType.GetProperty("CanConvert").GetGetMethod().Invoke(instance, null); } } 

Your use might look like this:

 object unknownObject = new Foo(); Type targetType = typeof(Bar); Type sourceType = unknownObject.GetType(); Console.WriteLine(TypeConverterChecker.Check(sourceType, targetType, unknownObject)); targetType = typeof(Nope); Console.WriteLine(TypeConverterChecker.Check(sourceType, targetType, unknownObject)); 
+7
source

I have implemented a solution that partially meets my requirements. It is based on a response from @ChrisSinclair, but does not require the provision of a sourceType object. Implementation:

  public static Boolean MatchResultTypeAndExpectedType(Type sourceType, Type targetType) { if (sourceType.IsValueType) return Check(sourceType, targetType); else return targetType.IsAssignableFrom(sourceType); } public static bool Check(Type fromType, Type toType) { Type converterType = typeof(TypeConverterChecker<,>).MakeGenericType(fromType, toType); object instance = Activator.CreateInstance(converterType); return (bool)converterType.GetProperty("CanConvert").GetGetMethod().Invoke(instance, null); } public class TypeConverterChecker<TFrom, TTo> { public bool CanConvert { get; private set; } public TypeConverterChecker() { TFrom from = default(TFrom); if (from == null) if (typeof(TFrom).Equals(typeof(String))) from = (TFrom)(dynamic)""; else from = (TFrom)Activator.CreateInstance(typeof(TFrom)); try { TTo to = (dynamic)from; CanConvert = true; } catch { CanConvert = false; } } } 

There are two problems in the solution:

  • It does not check if implicit conversions exist for types without value (for example, user-defined). Because IsAssignableFrom used, for non-value types, only inheritance applies.
  • It does not cover value types, which is null by default and has no default constructors other than String . String explicitly covered.
+1
source

Source: https://habr.com/ru/post/1491652/


All Articles