Comment puis-je déterminer si une conversion implicite existe en C #?

J’ai deux types, T et U, et je veux savoir si un opérateur de conversion implicite est défini de T à U.

Je suis conscient de l’existence de IsAssignableFrom , et ce n’est pas ce que je recherche, car il ne traite pas des conversions implicites.

Un peu de recherche sur Google m’a conduit à cette solution , mais dans les propres mots de l’auteur, il s’agit d’un code laid (il essaie de lancer implicitement et renvoie false s’il y a une exception, true sinon …)

Il semble que tester l’existence d’une méthode op_Implicit avec la signature correcte ne fonctionnera pas pour les types primitifs .

Existe-t-il un moyen plus simple de déterminer l’existence d’un opérateur de conversion implicite?

Vous pouvez utiliser la reflection pour trouver la méthode de conversion implicite pour le type de cible:

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

Vous pouvez l’utiliser comme ceci:

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

Notez que cela ne vérifie qu’une conversion de type implicite sur le type de base (le premier type transmis). Techniquement, la conversion de type peut également être définie sur l’autre type. Vous devrez peut-être l’appeler à nouveau avec les types inversés (ou l’intégrer dans la méthode). Les conversions de types implicites peuvent toutefois ne pas exister sur les deux types.

J’ai fini par gérer manuellement le scénario des types primitifs. Pas très élégant, mais ça marche.

J’ai également ajouté une logique supplémentaire pour gérer les types et les énumérations nullables.

J’ai réutilisé le code de Poke pour le scénario de type défini par l’utilisateur.

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

Voici une solution que j’ai trouvée. Le code majeur affiché ci-dessous (après une traduction simple):

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

Mise à jour: Voici le fichier entier.