我有两种类型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)));
结果是:
Int16 to Decimal - False
UInt16 to Decimal - False
UInt16 to Int64 - False
UInt16 to Double - False
UInt16 to String - False
[编辑] 所以我的问题:
在.NET中是否有办法检查给定类型的值是否可以在不知道值的情况下分配给另一种类型的变量,例如,隐式转换是否会成功?实施MatchResultTypeAndExpectedType(Type sourceType, Type targetType)
的更具体要求:
众所周知sourceType
是否为值类型。所以方法的签名可以像MatchResultTypeAndExpectedType(Type sourceType, Boolean isSourceValueType, Type targetType)
一种方法是实施Implicit Numeric Conversions Table,但不会涵盖其他转化或用户定义的转化。
答案 0 :(得分: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;
}
}
}
给出一些类:
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
{
}
用法:
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
根据您测试的类型:
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
现在我可以想象这可以被修改为更高效(静态缓存结果,因此后续查找相同的TFrom, TTo
组合不必尝试转换,因为值类型忽略了对要投射的输入实例(只使用default(TFrom)
)等等,但它应该给你一个开始。应该注意你应该不传入null
for {{ 1}}因为所有TFrom from
次转换都会通过(除非是转换为值类型)
您还可以添加第二个null
以尝试使用Convert.ChangeType方法,并查看这些类型是否已定义可以利用的try/catch
实施。 (您可能希望将其存储为单独的布尔标志,以便您知道以后需要执行哪种类型的转换)
编辑:如果你在编译时不知道类型,你可以利用一些反思仍然利用转换检查器:
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);
}
}
答案 1 :(得分:1)
我实施了一个解决方案,它部分满足了我的要求。它基于@ChrisSinclair的答案,但不需要提供sourceType
的对象。
实施是:
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;
}
}
}
解决方案存在两个问题:
IsAssignableFrom
,因此只对非值类型包含继承。String
之外没有默认构造函数。明确涵盖String
。答案 2 :(得分:0)
您始终可以这样做:
try
{
Convert.ChangeType(val, typeof(targetType));
return true;
}
catch (Exception)
{
return false;
}
我知道您没有该实例,但是您可以使用以下方法轻松,非常便宜地创建该实例:
var val = Activator.CreateInstance(sourceType);
请注意,对于值类型,Activator.CreateInstance()非常快。
当然,对于引用类型,只需使用Type.IsInstanceOfType()。