如何确定C#中是否存在隐式转换?

我有两种类型,T和U,我想知道是否从T到U定义了隐式强制转换运算符。

我知道IsAssignableFrom的存在,这不是我正在寻找的,因为它不处理隐式转换。

一些谷歌搜索引导我到这个解决方案 ,但在作者自己的话中,这是丑陋的代码(它试图隐式转换,如果有exception则返回false,否则为true)

似乎测试是否存在具有正确签名的op_Implicit方法将不适用于基本类型 。

是否有更简洁的方法来确定隐式转换运算符的存在?

您可以使用reflection来查找目标类型的隐式转换方法:

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; }); } 

你可以像这样使用它:

 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)); 

请注意,这仅检查基类型(第一个传递的类型)的隐式类型转换。 从技术上讲,类型转换也可以在另一种类型上定义,因此您可能需要使用反转类型(或将其构建到方法中)再次调用它。 但是,两种类型可能不存在隐式类型转换。

我最终手动处理了原始类型场景。 不是很优雅,但它的工作原理。

我还添加了额外的逻辑来处理可空类型和枚举。

我重用Poke的代码用于用户定义的类型场景。

 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 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> ImplicitNumericConversions = new Dictionary>(); static AvailableCastChecker() { ImplicitNumericConversions.Add(typeof(sbyte), new List {typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(byte), new List { typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(short), new List { typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(ushort), new List { typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(int), new List { typeof(long), typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(uint), new List { typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(long), new List { typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(char), new List { typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) }); ImplicitNumericConversions.Add(typeof(float), new List { typeof(double) }); ImplicitNumericConversions.Add(typeof(ulong), new List { 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; }); } } 

这是我找到的解决方案。 主要代码如下所示(经过一些简单的翻译):

 public static bool IsImplicitFrom(this Type type, Type fromType) { if (type == null || fromType == null) { return false; } // support for reference type if (type.IsByRef) { type = type.GetElementType(); } if (fromType.IsByRef) { fromType = type.GetElementType(); } // could always be convert to object if (type.Equals(typeof(object))) { return true; } // check if it could be convert using standard implicit cast if (IsStandardImplicitFrom(type, fromType)) { return true; } // determine implicit convert operator Type nonNullalbeType, nonNullableFromType; if (IsNullableType(type, out nonNullalbeType) && IsNullableType(fromType, out nonNullableFromType)) { type = nonNullalbeType; fromType = nonNullableFromType; } return ConversionCache.GetImplicitConversion(fromType, type) != null; } internal static bool IsStandardImplicitFrom(this Type type, Type fromType) { // support for Nullable if (!type.IsValueType || IsNullableType(ref type)) { fromType = GetNonNullableType(fromType); } // determine implicit value type convert HashSet typeSet; if (!type.IsEnum && ImplicitNumericConversions.TryGetValue(Type.GetTypeCode(type), out typeSet)) { if (!fromType.IsEnum && typeSet.Contains(Type.GetTypeCode(fromType))) { return true; } } // determine implicit reference type convert and boxing convert return type.IsAssignableFrom(fromType); } 

更新: 这是整个文件。