Como verifico se um tipo é um subtipo OU o tipo de um objeto?

335

Para verificar se um tipo é uma subclasse de outro tipo em C #, é fácil:

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

No entanto, isso irá falhar:

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

Existe alguma maneira de verificar se um tipo é uma subclasse OU da própria classe base, sem usar um ORoperador ou usar um método de extensão?

Daniel T.
fonte

Respostas:

499

Aparentemente não.

Aqui estão as opções:

Type.IsSubclassOf

Como você já descobriu, isso não funcionará se os dois tipos forem iguais. Aqui está um exemplo de programa LINQPad que demonstra:

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

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

Resultado:

True
False

O que indica que Derivedé uma subclasse de Base, mas que Base(obviamente) não é uma subclasse de si mesma.

Type.IsAssignableFrom

Agora, isso responderá sua pergunta em particular, mas também fornecerá falsos positivos. Como Eric Lippert apontou nos comentários, embora o método realmente retorne Truepara as duas perguntas acima, ele também retornará Truepara estas, que você provavelmente não deseja:

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 { }

Aqui você obtém a seguinte saída:

True
True
True

O último Trueindica que, se o método respondeu apenas à pergunta, é uint[]herdado int[]ou é do mesmo tipo, o que claramente não é o caso.

Portanto, IsAssignableFromtambém não está totalmente correto.

is e as

O "problema" com ise asno contexto de sua pergunta é que eles exigirão que você opere nos objetos e grave um dos tipos diretamente no código, e não trabalhe com Typeobjetos.

Em outras palavras, isso não será compilado:

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

nem isso:

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

nem isso:

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

Conclusão

Embora os métodos acima possam atender às suas necessidades, a única resposta correta para sua pergunta (como eu a vejo) é que você precisará de uma verificação extra:

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

o que obviamente faz mais sentido em um método:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
Lasse V. Karlsen
fonte
2
Obrigado! Marcarei isso como a resposta correta (tenho que esperar mais 8 minutos), pois você mencionou que a verificação deve ser revertida e forneceu um link para a documentação do MSDN.
Daniel T.
71
Observe que isso realmente não faz o que a pergunta pediu; isso não determina se um tipo é uma subclasse de outro, mas se um tipo é uma atribuição compatível com outro. Uma matriz de uint não é uma subclasse de uma matriz de int, mas são compatíveis com a atribuição. IEnumerable <Giraffe> não é uma subclasse de IEnumerable <Animal>, mas elas são compatíveis com a atribuição na v4.
quer
Não há como fazer isso no nome do método como Java? `` void <? estende Base> saveObject (? objectToSave) `` ``
Oliver Dixon
2
Como se IsInstanceOfTypeencaixaria nisso?
Lennart
Pode ser tentador converter o IsSameOrSubclass em um método de extensão, mas eu recomendo que seja um pouco estranho ler e escrever e fácil de bagunçar (reverter a ordem de potencialBase e potencial Descendente pode ser mortal).
JRH
36
typeof(BaseClass).IsAssignableFrom(unknownType);
Marc Gravell
fonte
1

Se você estiver tentando fazer isso em um projeto PCL do Xamarin Forms, as soluções acima serão exibidas IsAssignableFrom:

Erro: 'Type' não contém uma definição para 'IsAssignableFrom' e não foi encontrado nenhum método de extensão 'IsAssignableFrom' que aceite um primeiro argumento do tipo 'Type' (está faltando uma diretiva de uso ou uma referência de montagem?)

porque IsAssignableFrompede um TypeInfoobjeto. Você pode usar o GetTypeInfo()método em System.Reflection:

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

Bruno Serrano
fonte
0

Estou postando esta resposta com a esperança de alguém compartilhar comigo se e por que seria uma má idéia. No meu aplicativo, eu tenho uma propriedade do tipo que eu quero verificar para ter certeza de que é typeof (A) ou typeof (B), onde B é qualquer classe derivada de A. Portanto, meu código:

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

Eu sinto que posso ser um pouco ingênuo aqui, então qualquer feedback será bem-vindo.

baskren
fonte
2
Existem alguns problemas com essa ideia: 1) o tipo precisa de um construtor sem parâmetros ou o CreateInstance falhará; 2) a conversão para (A) não retorna nulo se a conversão não puder ser feita, ela será lançada; 3) você realmente não precisa da nova instância, então você tem uma alocação inútil. A resposta aceita é melhor (embora não perfeita).
Marcel Popescu
Obrigado pelo feedback. Muito útil.
baskren
@ baskren, talvez até vote seu comentário útil. Eu estava na votação número 1.
Developer63