Come posso verificare se un tipo è un sottotipo O il tipo di un oggetto?


335

Per verificare se un tipo è una sottoclasse di un altro tipo in C #, è facile:

typeof (SubClass).IsSubclassOf(typeof (BaseClass)); // returns true

Tuttavia, ciò fallirà:

typeof (BaseClass).IsSubclassOf(typeof (BaseClass)); // returns false

Esiste un modo per verificare se un tipo è una sottoclasse OR della classe base stessa, senza utilizzare un ORoperatore o un metodo di estensione?

Risposte:


499

Apparentemente no.

Ecco le opzioni:

Type.IsSubclassOf

Come hai già scoperto, questo non funzionerà se i due tipi sono uguali, ecco un esempio di programma LINQPad che dimostra:

void Main()
{
    typeof(Derived).IsSubclassOf(typeof(Base)).Dump();
    typeof(Base).IsSubclassOf(typeof(Base)).Dump();
}

public class Base { }
public class Derived : Base { }

Produzione:

True
False

Il che indica che Derivedè una sottoclasse di Base, ma che non Baseè (ovviamente) una sottoclasse di se stessa.

Type.IsAssignableFrom

Ora, questo risponderà alla tua domanda particolare, ma ti darà anche falsi positivi. Come ha sottolineato Eric Lippert nei commenti, mentre il metodo tornerà effettivamente Trueper le due domande precedenti, tornerà anche Trueper queste, che probabilmente non vuoi:

void Main()
{
    typeof(Base).IsAssignableFrom(typeof(Derived)).Dump();
    typeof(Base).IsAssignableFrom(typeof(Base)).Dump();
    typeof(int[]).IsAssignableFrom(typeof(uint[])).Dump();
}

public class Base { }
public class Derived : Base { }

Qui ottieni il seguente output:

True
True
True

L'ultimo Trueindicherebbe, se il metodo rispondesse solo alla domanda posta, che uint[]eredita int[]o che sono dello stesso tipo, il che chiaramente non è il caso.

Quindi IsAssignableFromnon è nemmeno del tutto corretto.

is e as

Il "problema" con ise asnel contesto della tua domanda è che ti richiederanno di operare sugli oggetti e scrivere uno dei tipi direttamente nel codice e non lavorare con gli Typeoggetti.

In altre parole, questo non verrà compilato:

SubClass is BaseClass
^--+---^
   |
   +-- need object reference here

né questo:

typeof(SubClass) is typeof(BaseClass)
                    ^-------+-------^
                            |
                            +-- need type name here, not Type object

né questo:

typeof(SubClass) is BaseClass
^------+-------^
       |
       +-- this returns a Type object, And "System.Type" does not
           inherit from BaseClass

Conclusione

Mentre i metodi di cui sopra potrebbero soddisfare le tue esigenze, l'unica risposta corretta alla tua domanda (come la vedo io) è che avrai bisogno di un controllo extra:

typeof(Derived).IsSubclassOf(typeof(Base)) || typeof(Derived) == typeof(Base);

che ovviamente ha più senso in un metodo:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}

2
Grazie! Contrassegnerò questa come la risposta corretta (devo aspettare altri 8 minuti) poiché hai detto che il controllo deve essere invertito e fornito un collegamento alla documentazione MSDN.
Daniel T.

71
Si noti che questo in realtà non fa ciò che la domanda è stata posta; ciò non determina se un tipo è una sottoclasse di un altro, ma piuttosto se un tipo è un'assegnazione compatibile con un altro. Una matrice di uint non è una sottoclasse di una matrice di int, ma sono compatibili con le assegnazioni. IEnumerable <Giraffe> non è una sottoclasse di IEnumerable <Animale>, ma sono compatibili con l'assegnazione in v4.
Eric Lippert,

Non c'è modo di farlo nel nome di metodo come Java? `` `void <? estende Base> saveObject (? objectToSave) `` `
Oliver Dixon il

2
Come si IsInstanceOfTypeadatterebbe a questo?
Lennart,

Potrebbe essere allettante convertire IsSameOrSubclass in un metodo di estensione, ma lo sconsiglio, è un po 'scomodo da leggere e scrivere e facile da incasinare (invertire l'ordine di potenzialeBase e potenzialeDescendente può essere mortale).
jrh



1

Se stai provando a farlo in un progetto PCL di Xamarin Forms, le soluzioni precedenti che usano IsAssignableFromdanno un errore:

Errore: "Tipo" non contiene una definizione per "IsAssignableFrom" e non è stato trovato alcun metodo di estensione "IsAssignableFrom" che accetta un primo argomento di tipo "Tipo" (manca una direttiva che utilizza o un riferimento di assembly?)

perché IsAssignableFromchiede un TypeInfooggetto. Puoi usare il GetTypeInfo()metodo da System.Reflection:

typeof(BaseClass).GetTypeInfo().IsAssignableFrom(typeof(unknownType).GetTypeInfo())


0

Sto postando questa risposta con la speranza che qualcuno condivida con me se e perché sarebbe una cattiva idea. Nella mia applicazione, ho una proprietà di tipo che voglio controllare per essere sicuro che sia typeof (A) o typeof (B), dove B è una classe derivata da A. Quindi il mio codice:

public class A
{
}

public class B : A
{
}

public class MyClass
{
    private Type _helperType;
    public Type HelperType
    {
        get { return _helperType; }
        set 
        {
            var testInstance = (A)Activator.CreateInstance(value);
            if (testInstance==null)
                throw new InvalidCastException("HelperType must be derived from A");
            _helperType = value;
        }
    }
}

Sento che potrei essere un po 'ingenuo qui, quindi qualsiasi feedback sarebbe il benvenuto.


2
Ci sono un paio di problemi con questa idea: 1) il tipo necessita di un costruttore senza parametri o CreateInstance fallirà; 2) il lancio su (A) non restituisce null se il cast non può essere effettuato, viene lanciato; 3) in realtà non hai bisogno della nuova istanza, quindi hai un'allocazione inutile. La risposta accettata è migliore (anche se non perfetta).
Marcel Popescu,

Grazie per il feedback. Molto utile.
baskren

@baskren, forse poi ha votato il suo utile commento. Sono stato valutato n. 1.
Developer63
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.