¿Cómo verifico si un tipo es un subtipo O el tipo de un objeto?

335

Para verificar si un tipo es una subclase de otro tipo en C #, es fácil:

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

Sin embargo, esto fallará:

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

¿Hay alguna manera de verificar si un tipo es una subclase O de la clase base en sí, sin usar un ORoperador o un método de extensión?

Daniel T.
fuente

Respuestas:

499

Aparentemente no.

Aquí están las opciones:

Type.IsSubclassOf

Como ya ha descubierto, esto no funcionará si los dos tipos son iguales, aquí hay un programa LINQPad de muestra que demuestra:

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

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

Salida:

True
False

Lo que indica que Derivedes una subclase de Base, pero que Base(obviamente) no es una subclase de sí misma.

Type.IsAssignableFrom

Ahora, esto responderá a su pregunta particular, pero también le dará falsos positivos. Como Eric Lippert ha señalado en los comentarios, si bien el método realmente regresará Truepara las dos preguntas anteriores, también regresará Truepara estas, que probablemente no desee:

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

Aquí obtienes el siguiente resultado:

True
True
True

Lo último Trueallí indicaría, si el método solo responde a la pregunta formulada, que uint[]hereda int[]o que son del mismo tipo, lo que claramente no es el caso.

Por IsAssignableFromlo tanto, tampoco es del todo correcto.

is y as

El "problema" con isy asen el contexto de su pregunta es que requerirán que opere en los objetos y escriba uno de los tipos directamente en el código, y no trabaje con Typeobjetos.

En otras palabras, esto no compilará:

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

ni esto:

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

ni esto:

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

Conclusión

Si bien los métodos anteriores pueden ajustarse a sus necesidades, la única respuesta correcta a su pregunta (tal como la veo) es que necesitará una verificación adicional:

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

que por supuesto tiene más sentido en un método:

public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
    return potentialDescendant.IsSubclassOf(potentialBase)
           || potentialDescendant == potentialBase;
}
Lasse V. Karlsen
fuente
2
¡Gracias! Marcaré esto como la respuesta correcta (tengo que esperar 8 minutos más) ya que mencionó que la verificación debe revertirse y proporcionó un enlace a la documentación de MSDN.
Daniel T.
71
Tenga en cuenta que esto en realidad no hace lo que la pregunta solicitó; esto no determina si un tipo es una subclase de otro, sino más bien si un tipo es una asignación compatible con otro. Una matriz de uint no es una subclase de una matriz de int, pero son compatibles con la asignación. IEnumerable <Giraffe> no es una subclase de IEnumerable <Animal>, pero son compatibles con la asignación en v4.
Eric Lippert
¿No hay forma de hacer esto en el nombre del método como Java? `` `nulo <? extiende Base> saveObject (? objectToSave) ``
Oliver Dixon
2
¿Cómo IsInstanceOfTypeencajaría en esto?
Lennart
Puede ser tentador convertir IsSameOrSubclass en un método de extensión, pero lo recomiendo, es un poco incómodo de leer y escribir, y fácil de estropear (invertir el orden de potencialBase y potencialDescendente puede ser mortal).
jrh
36
typeof(BaseClass).IsAssignableFrom(unknownType);
Marc Gravell
fuente
1

Si está intentando hacerlo en un proyecto PCL de Xamarin Forms, las soluciones anteriores que se utilizan IsAssignableFromdan un error:

Error: 'Tipo' no contiene una definición para 'IsAssignableFrom' y no se puede encontrar ningún método de extensión 'IsAssignableFrom' que acepte un primer argumento de tipo 'Tipo' (¿falta una directiva de uso o una referencia de ensamblado?)

porque IsAssignableFrompide un TypeInfoobjeto Puedes usar el GetTypeInfo()método desde System.Reflection:

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

Bruno Serrano
fuente
0

Estoy publicando esta respuesta con la esperanza de que alguien comparta conmigo si y por qué sería una mala idea. En mi aplicación, tengo una propiedad de Tipo que quiero verificar para asegurarme de que es typeof (A) o typeof (B), donde B es cualquier clase derivada de A. Entonces mi 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;
        }
    }
}

Siento que podría ser un poco ingenuo aquí, por lo que cualquier comentario sería bienvenido.

baskren
fuente
2
Hay un par de problemas con esa idea: 1) el tipo necesita un constructor sin parámetros o CreateInstance fallará; 2) lanzar a (A) no devuelve nulo si no se puede lanzar, lanza; 3) en realidad no necesita la nueva instancia, por lo que tiene una asignación inútil. La respuesta aceptada es mejor (aunque no perfecta).
Marcel Popescu
Gracias por la respuesta. Muy útil.
baskren
@baskren, tal vez luego votó su útil comentario. Me votaron a favor # 1.
Desarrollador63