I have two types sourceType
and targetType
and I need to write a method in C#, which checks if values of sourceType
can be assigned to a variable of targetType
. The signature of the function is MatchResultTypeAndExpectedType(Type sourceType, Type targetType)
.
The inheritance is covered by IsAssignableFrom. In the case of convertible types I thought to use CanConvertFrom, but, for example, if both types are numerical, then it always returns false
.
A test, which 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)));
The result is:
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 there a way in .NET to check whether a value of a given type can be assigned to a variable of another type without knowing values, e.g., whether implicit conversion will succeed? More specific requirements for implementation of MatchResultTypeAndExpectedType(Type sourceType, Type targetType)
:
It is known whether sourceType
is value type. So the signature of the method can be like MatchResultTypeAndExpectedType(Type sourceType, Boolean isSourceValueType, Type targetType)
One way is to implement Implicit Numeric Conversions Table, but it will not cover other conversions or user defined conversions.
The problem about implicit/explicit conversions is that they're resolved at compile time. So there is (as far as I know) no simple runtime check. However, the dynamic
implementation will pick them out and invoke them at runtime. You can (however ugly) create a class which will attempt to perform the conversion, catch the exception if it fails, and report whether or not it passed:
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;
}
}
}
Given some classes like:
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
{
}
Usage:
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 tested:
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 this can be modified to be more efficient (cache the result statically so subsequent lookups for the same TFrom, TTo
combination don't have to attempt the conversion, for value-types ignore the need for an input instance to cast (just use default(TFrom)
) and so on, but it should give you a start. It should be noted that you should not pass in null
for TFrom from
as all null
conversions will pass (unless it's to a value-type)
You can also add a second try/catch
to attempt using the Convert.ChangeType method and see if the types have defined IConvertable
implementations that can be leveraged. (you may want to store this as a separate boolean flag so you know which type of conversion you need to perform later)
EDIT: If you don't know the types at compile time, you can take advantage of a bit of reflection to still leverage 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 usage might be like:
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));
I implemented a solution, which partially satisfy my requirements. It is based on the answer from @ChrisSinclair, but does not require to provide an object of sourceType
.
The implementation is:
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 with the solution:
IsAssignableFrom
is used, only inheritance is covered for non-value types.String
. String
is explicitly covered.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With