【问题标题】:How can I determine if an implicit cast exists in C#?如何确定 C# 中是否存在隐式强制转换?
【发布时间】:2015-11-08 14:28:53
【问题描述】:

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

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

我在谷歌上搜索了一下this solution,但用作者自己的话来说,这是丑陋的代码(它会尝试隐式转换,如果有异常则返回 false,否则返回 true...)

似乎在测试是否存在具有正确签名 won't work for primitive types 的 op_Implicit 方法。

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

【问题讨论】:

  • 只是一个提示:看看implicit type conversion operators。我想应该有一种方法可以通过反射找到隐式运算符...
  • 您能否详细说明您要通过结果实现的目标?过去我遇到过类似的问题,意识到它几乎必须看起来像 CliveDM linked below,并决定只调用 Convert.ChangeType 并处理异常。我意识到在您的情况下这可能不是一个可行的解决方案,但也许有类似的解决方法。

标签: c# .net reflection casting implicit-conversion


【解决方案1】:

您可以使用反射找到目标类型的隐式转换方法:

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

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

【讨论】:

  • 它似乎不适用于原始类型,例如 MappingsGetter.HasImplicitConversion(typeof (int), typeof (decimal)) 返回 false 而存在隐式转换:msdn.microsoft.com/en-us/library/y5b434w4.aspx
  • @Brann 这些类型没有隐式运算符;类型转换由 CLR 直接完成。他们确实实现了IConvertible,所以你可以测试一下。
  • @Brann 对于原始类型,您必须在某处硬编码一个表。我不知道有任何内置机制可以让您以编程方式执行此操作。
  • @Kyle:我刚刚完成并发布了这个作为答案。不优雅,但它可以完成工作。
【解决方案2】:

我最终手动处理了原始类型场景。不是很优雅,但很有效。

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

我将Poke's code 重用于用户自定义类型场景。

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<Type> 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<Type,List<Type>> ImplicitNumericConversions = new Dictionary<Type, List<Type>>();

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

    }
}

【讨论】:

  • 枚举处理令人惊讶,因为它似乎没有遵循与其他一切相同的规则。有关详细信息,请参阅hastebin.com/rexuzaraju。也许这没关系,但它至少是原始问题中遗漏的内容。
  • 在字典中使用的正确类型是HashSet
【解决方案3】:

Here's 我找到的解决方案。主要代码如下(经过一些简单的翻译):

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<T>
    if (!type.IsValueType || IsNullableType(ref type)) {
        fromType = GetNonNullableType(fromType);
    }

    // determine implicit value type convert
    HashSet<TypeCode> 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);
}

更新: Here's整个文件。

【讨论】:

  • if (fromType.IsByRef) { fromType = type.GetElementType(); } 在错误的变量上调用 GetElementType
  • ImplicitNumericConversionsConversionCache 是从哪里来的?
  • @CliveDM,您可能修复了“整个文件”参考中的 ImplicitNumericConversions 参考。但 ConversionCache 仍然是一个悬空引用。
猜你喜欢
  • 1970-01-01
  • 2012-03-22
  • 1970-01-01
  • 1970-01-01
  • 2019-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多