sirve - typeof expression c#
Comprobación de tipo: typeof, GetType, o es? (14)
Se utiliza para obtener el objeto System.Type para un tipo. Una expresión typeof toma la siguiente forma:
System.Type type = typeof(int);
Example:
public class ExampleClass
{
public int sampleMember;
public void SampleMethod() {}
static void Main()
{
Type t = typeof(ExampleClass);
// Alternatively, you could use
// ExampleClass obj = new ExampleClass();
// Type t = obj.GetType();
Console.WriteLine("Methods:");
System.Reflection.MethodInfo[] methodInfo = t.GetMethods();
foreach (System.Reflection.MethodInfo mInfo in methodInfo)
Console.WriteLine(mInfo.ToString());
Console.WriteLine("Members:");
System.Reflection.MemberInfo[] memberInfo = t.GetMembers();
foreach (System.Reflection.MemberInfo mInfo in memberInfo)
Console.WriteLine(mInfo.ToString());
}
}
/*
Output:
Methods:
Void SampleMethod()
System.String ToString()
Boolean Equals(System.Object)
Int32 GetHashCode()
System.Type GetType()
Members:
Void SampleMethod()
System.String ToString()
Boolean Equals(System.Object)
Int32 GetHashCode()
System.Type GetType()
Void .ctor()
Int32 sampleMember
*/
Esta muestra utiliza el método GetType para determinar el tipo que se utiliza para contener el resultado de un cálculo numérico. Esto depende de los requisitos de almacenamiento del número resultante.
class GetTypeTest
{
static void Main()
{
int radius = 3;
Console.WriteLine("Area = {0}", radius * radius * Math.PI);
Console.WriteLine("The type is {0}",
(radius * radius * Math.PI).GetType()
);
}
}
/*
Output:
Area = 28.2743338823081
The type is System.Double
*/
He visto a muchas personas usar el siguiente código:
Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Pero sé que también podrías hacer esto:
if (obj1.GetType() == typeof(int))
// Some code here
O esto:
if (obj1 is int)
// Some code here
Personalmente, siento que el último es el más limpio, pero ¿hay algo que me esté perdiendo? ¿Cuál es el mejor para usar, o es la preferencia personal?
Creo que la última también analiza la herencia (por ejemplo, Dog is Animal == true), que es mejor en la mayoría de los casos.
Depende de lo que esté haciendo. Si necesito un valor bool (por ejemplo, para determinar si lanzaré a un int), usaré is
. Si realmente necesito el tipo por alguna razón (por ejemplo, para pasar a otro método) GetType()
.
El último es más limpio, más obvio y también verifica los subtipos. Los otros no comprueban el polimorfismo.
Prueba de rendimiento typeof () vs GetType ():
using System;
namespace ConsoleApplication1
{
class Program
{
enum TestEnum { E1, E2, E3 }
static void Main(string[] args)
{
{
var start = DateTime.UtcNow;
for (var i = 0; i < 1000000000; i++)
Test1(TestEnum.E2);
Console.WriteLine(DateTime.UtcNow - start);
}
{
var start = DateTime.UtcNow;
for (var i = 0; i < 1000000000; i++)
Test2(TestEnum.E2);
Console.WriteLine(DateTime.UtcNow - start);
}
Console.ReadLine();
}
static Type Test1<T>(T value) => typeof(T);
static Type Test2(object value) => value.GetType();
}
}
Resultados en el modo de depuración:
00:00:08.4096636
00:00:10.8570657
Resultados en modo de lanzamiento:
00:00:02.3799048
00:00:07.1797128
Puede usar el operador "typeof ()" en C # pero necesita llamar al espacio de nombres usando System.IO; Debe usar la palabra clave "is" si desea verificar un tipo.
Si está utilizando C # 7, es hora de actualizar la excelente respuesta de Andrew Hare. La coincidencia de patrones ha introducido un método abreviado agradable que nos da una variable escrita en el contexto de la sentencia if, sin requerir una declaración / conversión y verificación por separado:
if (obj1 is int integerValue)
{
integerValue++;
}
Esto parece bastante decepcionante para un elenco como este, pero realmente brilla cuando tienes muchos tipos posibles en tu rutina. La siguiente es la vieja forma de evitar lanzar dos veces:
Button button = obj1 as Button;
if (button != null)
{
// do stuff...
return;
}
TextBox text = obj1 as TextBox;
if (text != null)
{
// do stuff...
return;
}
Label label = obj1 as Label;
if (label != null)
{
// do stuff...
return;
}
// ... and so on
Trabajar alrededor de reducir este código tanto como sea posible, así como evitar las repeticiones duplicadas del mismo objeto siempre me ha molestado. Lo anterior está muy bien comprimido con un patrón que coincide con lo siguiente:
switch (obj1)
{
case Button button:
// do stuff...
break;
case TextBox text:
// do stuff...
break;
case Label label:
// do stuff...
break;
// and so on...
}
EDITAR: Se actualizó el nuevo método más largo para usar un interruptor según el comentario de Palec.
Tenía una propiedad de Type
con la que comparar y no podía usar (como my_type is _BaseTypetoLookFor
), pero podría usar estos:
base_type.IsInstanceOfType(derived_object);
base_type.IsAssignableFrom(derived_type);
derived_type.IsSubClassOf(base_type);
Observe que IsInstanceOfType
y IsAssignableFrom
devuelven true
cuando se comparan los mismos tipos, donde IsSubClassOf devolverá false
. E IsSubclassOf
no funciona en las interfaces, donde lo hacen los otros dos. (Ver también esta pregunta y respuesta ).
public class Animal {}
public interface ITrainable {}
public class Dog : Animal, ITrainable{}
Animal dog = new Dog();
typeof(Animal).IsInstanceOfType(dog); // true
typeof(Dog).IsInstanceOfType(dog); // true
typeof(ITrainable).IsInstanceOfType(dog); // true
typeof(Animal).IsAssignableFrom(dog.GetType()); // true
typeof(Dog).IsAssignableFrom(dog.GetType()); // true
typeof(ITrainable).IsAssignableFrom(dog.GetType()); // true
dog.GetType().IsSubclassOf(typeof(Animal)); // true
dog.GetType().IsSubclassOf(typeof(Dog)); // false
dog.GetType().IsSubclassOf(typeof(ITrainable)); // false
Todos son diferentes.
-
typeof
toma un nombre de tipo (que especifica en tiempo de compilación). -
GetType
obtiene el tipo de tiempo de ejecución de una instancia. - devuelve true si una instancia está en el árbol de herencia.
Ejemplo
class Animal { }
class Dog : Animal { }
void PrintTypes(Animal a) {
Console.WriteLine(a.GetType() == typeof(Animal)); // false
Console.WriteLine(a is Animal); // true
Console.WriteLine(a.GetType() == typeof(Dog)); // true
Console.WriteLine(a is Dog); // true
}
Dog spot = new Dog();
PrintTypes(spot);
¿Qué pasa con
typeof(T)
? ¿También se resuelve en tiempo de compilación?
Sí. T es siempre lo que el tipo de expresión es. Recuerde, un método genérico es básicamente un montón de métodos con el tipo apropiado. Ejemplo:
string Foo<T>(T parameter) { return typeof(T).Name; }
Animal probably_a_dog = new Dog();
Dog definitely_a_dog = new Dog();
Foo(probably_a_dog); // this calls Foo<Animal> and returns "Animal"
Foo<Animal>(probably_a_dog); // this is exactly the same as above
Foo<Dog>(probably_a_dog); // !!! This will not compile. The parameter expects a Dog, you cannot pass in an Animal.
Foo(definitely_a_dog); // this calls Foo<Dog> and returns "Dog"
Foo<Dog>(definitely_a_dog); // this is exactly the same as above.
Foo<Animal>(definitely_a_dog); // this calls Foo<Animal> and returns "Animal".
Foo((Animal)definitely_a_dog); // this does the same as above, returns "Animal"
Utilice typeof
cuando desee obtener el tipo en tiempo de compilación . Utilice GetType
cuando desee obtener el tipo en el momento de la ejecución . Rara vez hay casos que usar, como lo es un cast y, en la mayoría de los casos, terminas lanzando la variable de todos modos.
Hay una cuarta opción que no ha considerado (especialmente si también va a lanzar un objeto al tipo que encuentre); que es usar as
.
Foo foo = obj as Foo;
if (foo != null)
// your code here
Esto solo usa un elenco mientras que este enfoque:
if (obj is Foo)
Foo foo = (Foo)obj;
requiere dos .
Yo prefiero es
Dicho esto, si estás usando es probable que no estés usando la herencia correctamente.
Supongamos que Persona: Entidad, y ese Animal: Entidad. Feed es un método virtual en Entity (para hacer feliz a Neil)
class Person
{
// A Person should be able to Feed
// another Entity, but they way he feeds
// each is different
public override void Feed( Entity e )
{
if( e is Person )
{
// feed me
}
else if( e is Animal )
{
// ruff
}
}
}
Más bien
class Person
{
public override void Feed( Person p )
{
// feed the person
}
public override void Feed( Animal a )
{
// feed the animal
}
}
1.
Type t = typeof(obj1);
if (t == typeof(int))
Esto es ilegal, porque typeof solo funciona en tipos, no en variables. Supongo que obj1 es una variable. Entonces, de esta manera, typeof es estático, y hace su trabajo en tiempo de compilación en lugar de en tiempo de ejecución.
2.
if (obj1.GetType() == typeof(int))
Esto es cierto si obj1 es exactamente de tipo int. Si obj1 se deriva de int, la condición if será falsa.
3.
if (obj1 is int)
Esto es cierto si obj1 es un int, o si se deriva de una clase llamada int, o si implementa una interfaz llamada int.
Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Esto es un error. El operador typeof en C # solo puede tomar nombres de tipo, no objetos.
if (obj1.GetType() == typeof(int))
// Some code here
Esto funcionará, pero tal vez no como usted esperaría. Para los tipos de valor, como se muestra aquí, es aceptable, pero para los tipos de referencia, solo devolvería verdadero si el tipo fuera exactamente el mismo tipo, no otra cosa en la jerarquía de herencia. Por ejemplo:
class Animal{}
class Dog : Animal{}
static void Foo(){
object o = new Dog();
if(o.GetType() == typeof(Animal))
Console.WriteLine("o is an animal");
Console.WriteLine("o is something else");
}
Esto imprimiría "o is something else"
, porque el tipo de o
es Dog
, no Animal
. Sin embargo, puede hacer que esto funcione si usa el método IsAssignableFrom
de la clase Type
.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
Sin embargo, esta técnica todavía deja un gran problema. Si su variable es nula, la llamada a GetType()
lanzará una NullReferenceException. Así que para que funcione correctamente, harías:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
Con esto, tienes un comportamiento equivalente de la palabra clave is
. Por lo tanto, si este es el comportamiento que desea, debe usar la palabra clave is
, que es más legible y más eficiente.
if(o is Animal)
Console.WriteLine("o is an animal");
Sin embargo, en la mayoría de los casos, la palabra clave is
todavía no es lo que realmente quiere, porque generalmente no es suficiente solo para saber que un objeto es de cierto tipo. Por lo general, realmente desea utilizar ese objeto como una instancia de ese tipo, lo que también requiere su conversión. Y entonces puedes encontrarte escribiendo un código como este:
if(o is Animal)
((Animal)o).Speak();
Pero eso hace que el CLR verifique el tipo de objeto hasta dos veces. Lo comprobará una vez para satisfacer el operador, y si o
es un Animal
, lo hacemos nuevamente para validar el modelo.
Es más eficiente hacer esto en su lugar:
Animal a = o as Animal;
if(a != null)
a.Speak();
El operador as
es un reparto que no lanzará una excepción si falla, en lugar de devolver un null
. De esta manera, el CLR verifica el tipo de objeto solo una vez, y después de eso, solo necesitamos hacer una verificación nula, que es más eficiente.
Pero cuidado: muchas personas caen en una trampa con as
. Debido a que no hace excepciones, algunas personas lo consideran un elenco "seguro" y lo usan exclusivamente, evitando los lanzamientos regulares. Esto conduce a errores como este:
(o as Animal).Speak();
En este caso, el desarrollador asume claramente que o
siempre será un Animal
, y siempre que su suposición sea correcta, todo funciona bien. Pero si están equivocados, entonces lo que terminan aquí es una NullReferenceException
. Con un reparto regular, habrían obtenido una InvalidCastException
lugar, lo que habría identificado más correctamente el problema.
A veces, este error puede ser difícil de encontrar:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
Este es otro caso en el que el desarrollador claramente espera que sea un Animal
cada vez, pero esto no es obvio en el constructor, donde se usa el modelo colado. No es obvio hasta que llegue al método Interact
, donde se espera que el campo animal
sea asignado de manera positiva. En este caso, no solo terminará con una excepción engañosa, sino que no se lanzará sino hasta mucho más tarde que cuando ocurrió el error real.
En resumen:
Si solo necesita saber si un objeto es o no de algún tipo, su uso
is
.Si necesita tratar un objeto como una instancia de un determinado tipo, pero no está seguro de que el objeto sea de ese tipo, utilice
as
y compruebe si hay unnull
.Si necesita tratar un objeto como una instancia de un determinado tipo, y se supone que el objeto es de ese tipo, use una conversión regular.
if (c is UserControl) c.Enabled = enable;