简体   繁体   English

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

[英]How can I determine if an implicit cast exists in C#?

I have two types, T and U, and I want to know whether an implicit cast operator is defined from T to U. 我有两种类型,T和U,我想知道是否从T到U定义了隐式强制转换运算符。

I'm aware of the existence of IsAssignableFrom , and this is not what I'm looking for, as it doesn't deal with implicit casts. 我知道IsAssignableFrom的存在,这不是我正在寻找的,因为它不涉及隐式转换。

A bit of googling led me to this solution , but in the author's own words this is ugly code (it tries to cast implicitly and returns false if there's an exception, true otherwise...) 一些谷歌搜索引导我到这个解决方案 ,但在作者自己的话这是丑陋的代码(它试图隐式转换,如果有异常则返回false,否则为true)

It seems testing for the existence of an op_Implicit method with the correct signature won't work for primitive types . 似乎测试是否存在具有正确签名的op_Implicit方法将不适用于基本类型

Is there a cleaner way of determining the existence of an implicit cast operator? 是否有更简洁的方法来确定隐式转换运算符的存在?

You could use reflection to find the implicit conversion method for the target type: 您可以使用反射来查找目标类型的隐式转换方法:

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

You can use it like this: 你可以像这样使用它:

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

Note that this only checks for an implicit type conversion on the base type (the first passed type). 请注意,这仅检查基类型(第一个传递的类型)的隐式类型转换。 Technically, the type conversion can also be defined on the other type, so you may need to call it again with the types reversed (or build that into the method). 从技术上讲,类型转换也可以在另一种类型上定义,因此您可能需要使用反转类型(或将其构建到方法中)再次调用它。 Implicit type conversions may not exist on both types though. 但两种类型可能不存在隐式类型转换。

I ended up handling the primitive types scenario manually. 我最终手动处理了原始类型场景。 Not very elegant, but it works. 不是很优雅,但它的工作原理。

I've also added additional logic to handle nullable types and enums. 我还添加了额外的逻辑来处理可空类型和枚举。

I reused Poke's code for the user-defined type scenario. 我将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<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;
            });

    }
}

Here's a solution I found. 这是我找到的解决方案。 The major code shown as bellow (after some simple translation): 主要代码如下所示(经过一些简单的翻译):

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

Update: Here's the whole file. 更新: 这是整个文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM