2010-01-22 13 views
15

Mam typ dwóch członków jako ciągi - a nie jako instancji typu. Jak mogę sprawdzić, czy te dwa typy można rzutować? Powiedzmy, że ciąg pierwszy to "System.Windows.Forms.Label", a drugi to "System.Windows.Forms.Control". Jak mogę sprawdzić, czy pierwsza z nich jest podklasą (lub niejawną rzutowaną) drugiej? Czy jest to możliwe dzięki użyciu refleksji?Sprawdź, czy typy można rzutować/podklasami

Dzięki za wsparcie!

+1

Skąd pochodzą te struny? Co próbujesz osiągnąć? – n8wrl

Odpowiedz

15

To może wydawać się należy użyć Type.IsAssignableFrom jednak pamiętać dokładnie dokumentację:

public virtual bool IsAssignableFrom(Type c)

true jeśli c i prąd [instancji z] Type reprezentują ten sam typ, lub jeśli prąd [ wystąpienie] Type jest w hierarchii dziedziczenia c, lub jeśli bieżący [instancja] Type jest interfejsem, który implementuje c, lub jeśli c jest parametrem typu ogólnego er i bieżący [instancja] Type reprezentuje jedno z ograniczeń c. false, jeśli żaden z tych warunków nie jest true, lub jeśli jest odniesieniem null (w języku Visual Basic).

W szczególności:

class Base { } 
clase NotABase { public static implicit operator Base(NotABase o) { // } } 

Console.WriteLine(typeof(Base).IsAssignableFrom(typeof(NotABase))); 

wypisze False na konsoli chociaż NotABase S są niejawnie rzutować na Base s. Tak więc, aby obsłużyć odlewania, możemy użyć refleksji tak:

static class TypeExtensions { 
    public static bool IsCastableTo(this Type from, Type to) { 
     if (to.IsAssignableFrom(from)) { 
      return true; 
     } 
     var methods = from.GetMethods(BindingFlags.Public | BindingFlags.Static) 
          .Where(
           m => m.ReturnType == to && 
            (m.Name == "op_Implicit" || 
            m.Name == "op_Explicit") 
         ); 
     return methods.Any(); 
    } 
} 

Zastosowanie:

Console.WriteLine(typeof(string).IsCastableTo(typeof(int))); // false 
Console.WriteLine(typeof(NotABase).IsCastableTo(typeof(Base))); // true 

A w Twoim przypadku

// from is string representing type name, e.g. "System.Windows.Forms.Label" 
// to is string representing type name, e.g. "System.Windows.Forms.Control" 
Type fromType = Type.GetType(from); 
Type toType = Type.GetType(to); 
bool castable = from.IsCastableTo(to); 
+4

'typeof (short) .IsCastableTo (typeof (int))' zwraca 'false', mimo że' short' jest niejawnie konwertowalne na 'int'. Czy istnieje sposób na to, aby Twoja metoda działała również w tym przypadku? –

+1

Świetne rozwiązanie. Dla kompletności, uważam, że powinieneś sprawdzić m.ReturnType.IsAssignable (to) zamiast sprawdzania równości. Oznacza to, że otrzymujesz niejawnych operatorów zwracających więcej typów pochodnych. Rzadki przypadek, ale warto go tam wrzucić. – Gusdor

+1

@Jason, tutaj jest mały problem, w którym powinieneś zapętlić oba typy, ponieważ operatory odlewania mogą być zdefiniowane na każdym typie. Również, jak Gusdor mówi "IsAssignableFrom" zajmie się odlewów możliwe poprzez dziedziczenie. Niemniej jednak doskonały starter. Spróbuję go rozszerzyć. – nawfal

6

Jeśli można przekonwertować te ciągi do Type obiektów następnie Twój najlepszy zakład to Type.IsAssignableFrom.

Należy jednak pamiętać, że tylko dwie instancje Type są zgodne na poziomie CLR. Nie uwzględnia to takich rzeczy, jak konwersje zdefiniowane przez użytkownika czy inna semantyka C#.

4

Jak o:

public bool IsCastable(String type0, String type1) 
{ 
    return Type.GetType(type1).IsAssignableFrom(Type.GetType(type0)); 
} 
+1

Należy zauważyć, że to faktycznie nie sprawdza, czy typ reprezentowany przez 'type0' można rzutować na typ reprezentowany przez' type1'. Twoja metoda wypisze 'false' dla klasy' Base {} class NotABase {publiczny statyczny niejawny operator Base (NotABase o) {// details}} 'nawet jeśli' NotABase' można rzutować na 'Base'.Ponieważ pytanie dotyczy postępowania w przypadku, gdy domniemany oddanie jest możliwe, zaniecham. – jason

0

Najprostszym sposobem jest value.GetType() IsSubclassOf (typeof (Control)) Zasadniczo metoda Type.IsSubclassOf zrobić co trzeba

1

To jest taka sama jak Jason. odpowiedź, ale rozwiązuje niektóre problemy z jego rozwiązaniem.

public static bool IsCastableTo(this Type from, Type to) 
{ 
    return to.IsAssignableFrom(from) 
     || to.GetConvertOperators().Any(m => m.GetParameters()[0].ParameterType.IsAssignableFrom(from)) 
     || from.GetConvertOperators(true).Any(m => to.IsAssignableFrom(m.ReturnType)); 
} 

public static IEnumerable<MethodInfo> GetConvertOperators(this Type type, bool lookInBase = false) 
{ 
    var bindinFlags = BindingFlags.Public 
        | BindingFlags.Static 
        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly); 
    return type.GetMethods(bindinFlags).Where(m => m.Name == "op_Implicit" || m.Name == "op_Explicit"); 
} 

ten powinien radzić sobie w sytuacjach, które powstają na skutek spadków, jak również.Na przykład:

class Mammal { public static implicit operator Car (Mammal o) { return null; } } 
class Cow : Mammal { } 
class Vehicle { } 
class Car : Vehicle { } 

tutaj związek ukryte wynosi Mammal i Car, ale ponieważ Cow jest Mammal również istnieją niejawnego konwersji z Cow do Car. Ale wszystkie są Car s; stąd Cow może przejść do Vehicle.

Cow c = null; 
Vehicle v = c; //legal 

Więc

typeof(Cow).IsCastableTo(typeof(Vehicle)); //true 

drukuje prawdziwe, choć nie bezpośredni operator konwersji istnieje między Cow i Vehicle.

powyżej rozwiązanie nie dla typów pierwotnych, gdzie konwersja jest wbudowany bezpośrednio w języku niż ramach, więc coś

typeof(short).IsCastableTo(typeof(int)); 

zawiedzie. Afaik, tylko ręczna obsługa pomoże. Otrzymasz pełną listę konwersji implicit i explicit dla typów liczbowych i other primitive types z msdn.

Edit:

Funkcja IsCastableTo mógłby być trochę bardziej "DRY" być może kosztem jest mniej czytelny, ale mi się podoba :)

public static bool IsCastableTo(this Type from, Type to) 
{ 
    return to.IsAssignableFrom(from) 
     || IsCastDefined(to, m => m.GetParameters()[0].ParameterType, _ => from, false) 
     || IsCastDefined(from, _ => to, m => m.ReturnType, true); 
} 

//little irrelevant DRY method 
static bool IsCastDefined(Type type, Func<MethodInfo, Type> baseType, Func<MethodInfo, Type> derivedType, 
          bool lookInBase) 
{ 
    var bindinFlags = BindingFlags.Public 
        | BindingFlags.Static 
        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly); 
    return type.GetMethods(bindinFlags).Any(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") 
               && baseType(m).IsAssignableFrom(derivedType(m))); 
} 
+0

co masz na myśli suchy? –

+0

Ponadto, nadal nie rozwiązuje problemu z int <==> krótkich lub długich –

+0

@LouisRhys suchych punktów do http://en.wikipedia.org/wiki/Don't_repeat_yourself które zaktualizowałem w odpowiedzi. W mojej pierwszej odpowiedzi używam funkcji 'IsAssignableFrom' dwa razy robiąc to samo mniej więcej. W drugim podejściu poddałem je refaktoryzacji jednej metodzie. Robi się trochę niepewnie, ale lubię to lepiej. – nawfal

5

Pomogła mi przez to dyskusja, dzięki.

Zmodyfikowałem kod nawfal, aby rozwiązać problem dotyczący typów pierwotnych.

Teraz zwraca poprawne wyniki.

typeof(short).IsCastableTo(typeof(int)); // True 
typeof(short).IsCastableTo(typeof(int), implicitly:true); // True 
typeof(int).IsCastableTo(typeof(short)); // True 
typeof(int).IsCastableTo(typeof(short), implicitly:true); // False 

Kod jest podany poniżej.

public static bool IsCastableTo(this Type from, Type to, bool implicitly = false) 
{ 
    return to.IsAssignableFrom(from) || from.HasCastDefined(to, implicitly); 
} 

static bool HasCastDefined(this Type from, Type to, bool implicitly) 
{ 
    if ((from.IsPrimitive || from.IsEnum) && (to.IsPrimitive || to.IsEnum)) 
    { 
     if (!implicitly) 
      return from==to || (from!=typeof(Boolean) && to!=typeof(Boolean)); 

     Type[][] typeHierarchy = { 
      new Type[] { typeof(Byte), typeof(SByte), typeof(Char) }, 
      new Type[] { typeof(Int16), typeof(UInt16) }, 
      new Type[] { typeof(Int32), typeof(UInt32) }, 
      new Type[] { typeof(Int64), typeof(UInt64) }, 
      new Type[] { typeof(Single) }, 
      new Type[] { typeof(Double) } 
     }; 
     IEnumerable<Type> lowerTypes = Enumerable.Empty<Type>(); 
     foreach (Type[] types in typeHierarchy) 
     { 
      if (types.Any(t => t == to)) 
       return lowerTypes.Any(t => t == from); 
      lowerTypes = lowerTypes.Concat(types); 
     } 

     return false; // IntPtr, UIntPtr, Enum, Boolean 
    } 
    return IsCastDefined(to, m => m.GetParameters()[0].ParameterType, _ => from, implicitly, false) 
     || IsCastDefined(from, _ => to, m => m.ReturnType, implicitly, true); 
} 

static bool IsCastDefined(Type type, Func<MethodInfo, Type> baseType, 
         Func<MethodInfo, Type> derivedType, bool implicitly, bool lookInBase) 
{ 
    var bindinFlags = BindingFlags.Public | BindingFlags.Static 
        | (lookInBase ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly); 
    return type.GetMethods(bindinFlags).Any(
     m => (m.Name=="op_Implicit" || (!implicitly && m.Name=="op_Explicit")) 
      && baseType(m).IsAssignableFrom(derivedType(m))); 
} 
+0

edytuj swoją odpowiedź, jeśli chcę coś zmienić. – flx

Powiązane problemy