Подтвердить что ты не робот

Как определить, существует ли неявный листинг в С#?

У меня есть два типа: T и U, и я хочу знать, определен ли оператор неявного литья из T в U.

Я знаю о существовании IsAssignableFrom, и это не то, что я ищу, так как это не" т с неявным отбрасыванием.

Немного googling привело меня к это решение, но в собственных словах автора это уродливый код (он пытается использовать неявно и возвращает false, если существует исключение, true в противном случае...)

Кажется, тестирование на существование метода op_Implicit с правильной подписью не будет работать для примитивных типов.

Существует ли более чистый способ определения существования неявного оператора литья?

4b9b3361

Ответ 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));

Обратите внимание, что это проверяет только неявное преобразование типа в базовом типе (первый переданный тип). Технически преобразование типа также может быть определено на другом типе, поэтому вам может потребоваться повторное его повторение с изменением типов (или построением их в методе). Неявные преобразования типов могут не существовать на обоих типах.

Ответ 2

В итоге я обработал сценарий примитивных типов вручную. Не очень элегантный, но он работает.

Я также добавил дополнительную логику для обработки типов и перечислений с нулевым значением.

Я повторно использовал Poke 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;
            });

    }
}

Ответ 3

Вот решение, которое я нашел. Основной код, показанный ниже (после простого перевода):

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

Обновление: Вот весь файл.