2015-08-15 9 views
19

Ich habe zwei Arten, T und U, und ich möchte wissen, ob ein impliziter Cast-Operator von T bis U.Wie kann ich feststellen, ob ein impliziter Cast in C# existiert?

definiert ist

Ich bin über die Existenz von IsAssignableFrom, und das ist nicht das, was ich bin suchen, da es sich nicht um implizite Casts handelt.

Ein bisschen googeln zu this solution führte mich, aber in den Worten des Autors ist dieses hässliche Code (versucht es implizit zu werfen und false zurückgibt, wenn eine Ausnahme gibt es, ansonsten true ...)

Es scheint Prüfung für das Vorhandensein einer op_Implicit-Methode mit der richtigen Signatur won't work for primitive types.

Gibt es eine sauberere Möglichkeit, die Existenz eines impliziten Cast-Operators zu bestimmen?

+0

Nur ein Tipp: Schauen Sie sich [implizite Typumwandlung Operatoren] (https://msdn.microsoft.com/en-us/library/z5z9kes2.aspx). Ich denke, es sollte einen Weg geben, implizite Operatoren durch Reflektion zu finden ... – elgonzo

+0

Kannst du bitte ausführlicher darüber sprechen, welches Ziel du mit dem Ergebnis erreichen willst? Ich hatte ein ähnliches Problem in der Vergangenheit, erkannte, dass es so aussehen müsste wie CliveDM [linked] (https://github.com/CYJB/Cyjb/blob/f4424c4f81cacd09e9ce5d202a03b0c121c09ac2/Cyjb/TypeExt.cs) [unten] ] (http://stackoverflow.com/a/32025388/1083771), und entschied sich, [Convert.ChangeType] (https://msdn.microsoft.com/en-us/library/dtb69x08.aspx) aufzurufen und zu verarbeiten Ausnahmen. Mir ist klar, dass das in Ihrem Fall keine praktikable Lösung ist, aber vielleicht gibt es eine ähnliche Problemumgehung. –

Antwort

2

Here's eine Lösung, die ich gefunden habe. Der Hauptcode als unten (nach einigen einfachen Übersetzung) gezeigt:

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 die gesamte Datei.

+1

Dieser Code scheint eher unvollständig zu sein. – poke

+1

@poke Ich aktualisierte meine Antwort, hoffentlich konnte es helfen. –

+0

'if (fromType.IsByRef) {fromType = type.GetElementType(); } 'ruft' GetElementType' für die falsche Variable auf. –

14

Sie Reflektion verwenden könnte die implizite Konvertierungsmethode für den Zieltyp zu finden:

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

Sie es wie folgt verwenden können:

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

Beachten Sie, dass dies nur prüft, ob eine implizite Typumwandlung auf dem Basistyp (der erste übergebene Typ). Technisch kann die Typkonvertierung auch für den anderen Typ definiert werden, so dass Sie sie möglicherweise erneut mit den umgekehrten Typen aufrufen müssen (oder diese in die Methode einbauen). Implizite Typkonvertierungen sind jedoch möglicherweise für beide Typen nicht verfügbar.

+0

Es scheint nicht für primitive Typen zu funktionieren, zB MappingsGetter.HasImplicitConversion (typeof (int), typeof (dezimal)) gibt false zurück, während eine implizite Konvertierung existiert: https://msdn.microsoft.com/de-de/library/y5b434w4.aspx – Brann

+3

@Brann Diese Typen haben keine impliziten Operatoren. Die Typumwandlung erfolgt direkt über die CLR. Sie implementieren jedoch ['IConvertible'] (https://msdn.microsoft.com/en-us/library/system.iconvertible.aspx), so dass Sie dafür testen können. – poke

+0

@Brann Für primitive Typen müssen Sie nur irgendwo eine Tabelle fest codieren. Ich kenne keinen integrierten Mechanismus, mit dem Sie dies programmgesteuert durchführen können. – Kyle

3

Ich habe am Ende das primitive Typen-Szenario manuell behandelt. Nicht sehr elegant, aber es funktioniert.

Ich habe auch zusätzliche Logik hinzugefügt, um Nullable Typen und enums zu behandeln.

Ich habe Poke's code für das benutzerdefinierte Typ Szenario wiederverwendet.

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

    } 
} 
+0

Das Enum-Handling war überraschend, da es anscheinend nicht den gleichen Regeln folgt wie alles andere. Siehe http://hastebin.com/rexuzaraju für Details. Vielleicht ist das in Ordnung, aber es ist minimal etwas, das von der ursprünglichen Frage ausgeschlossen wurde. –

+0

Der richtige Typ im Wörterbuch ist HashSet – sam