如何检查类型是否可以转换为C#中的其他类型

k_r*_*rus 6 c# type-conversion

我有两种类型sourceType,targetType我需要在C#中编写一个方法,它检查是否sourceType可以将值赋值给变量targetType.功能的签名是MatchResultTypeAndExpectedType(Type sourceType, Type targetType).

继承由IsAssignableFrom涵盖.在可转换类型的情况下,我认为使用CanConvertFrom,但是,例如,如果两个类型都是数字,那么它总是返回false.我执行的测试:

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)));
Run Code Online (Sandbox Code Playgroud)

结果是:

Int16 to Decimal - False
UInt16 to Decimal - False
UInt16 to Int64 - False
UInt16 to Double - False
UInt16 to String - False
Run Code Online (Sandbox Code Playgroud)

[编辑]所以我的问题:在.NET中是否有办法检查给定类型的值是否可以在不知道值的情况下分配给另一种类型的变量,例如,隐式转换是否会成功?实施的更具体要求MatchResultTypeAndExpectedType(Type sourceType, Type targetType):

  1. 源和目标类型在编译时是未知的,因为它们的程序集稍后会加载.
  2. 不能提供任何值或对象作为输入.
  3. 在实现中不能创建类型的对象,因为系统的其余部分不允许这样做.可以创建值类型的值.
  4. 只需要检查隐式转换.

众所周知,是否sourceType是价值类型.所以方法的签名就像MatchResultTypeAndExpectedType(Type sourceType, Boolean isSourceValueType, Type targetType)

一种方法是实现隐式数字转换表,但不包括其他转换或用户定义的转换.

Chr*_*air 9

隐式/显式转换的问题是它们在编译时被解析.所以(据我所知)没有简单的运行时检查.但是,dynamic实现选择它们并在运行时调用它们.您可以(无论如何丑陋)创建一个将尝试执行转换的类,如果失败则捕获异常,并报告它是否通过:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

给出了一些类:

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
{

}
Run Code Online (Sandbox Code Playgroud)

用法:

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
Run Code Online (Sandbox Code Playgroud)

根据您测试的类型:

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
Run Code Online (Sandbox Code Playgroud)

现在我可以想象这可以被修改为更高效(静态缓存结果,因此后续查找相同的TFrom, TTo组合不必尝试转换,因为值类型忽略了对输入实例进行转换的需要(只是使用default(TFrom))等等,但它应该给你一个开始.应该注意的是,你应该传递nullTFrom from,因为所有null的转换将通过(除非它是一个价值型)

您还可以添加一秒try/catch来尝试使用Convert.ChangeType方法,并查看类型是否已定义IConvertable可以利用的实现.(您可能希望将其存储为单独的布尔标志,以便您知道以后需要执行哪种类型的转换)

编辑:如果您在编译时不知道类型,您可以利用一些反思仍然利用转换检查器:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您的用法可能如下:

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));
Run Code Online (Sandbox Code Playgroud)