Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Questo è un errore L'operatore typeof in C # può accettare solo nomi di tipi, non oggetti.
if (obj1.GetType() == typeof(int))
// Some code here
Funzionerà, ma forse non come ti aspetteresti. Per i tipi di valore, come mostrato qui, è accettabile, ma per i tipi di riferimento, restituisce true solo se il tipo era dello stesso tipo esatto , non qualcos'altro nella gerarchia dell'ereditarietà. Per esempio:
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");
}
Questo verrebbe stampato "o is something else"
, perché il tipo di o
è Dog
, non Animal
. Puoi farlo funzionare, tuttavia, se usi il IsAssignableFrom
metodo della Type
classe.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
Questa tecnica lascia comunque un grosso problema. Se la variabile è nulla, la chiamata a GetType()
genererà una NullReferenceException. Quindi, per farlo funzionare correttamente, dovresti fare:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
Con questo, hai un comportamento equivalente della is
parola chiave. Pertanto, se questo è il comportamento desiderato, è necessario utilizzare la is
parola chiave, che è più leggibile e più efficiente.
if(o is Animal)
Console.WriteLine("o is an animal");
Nella maggior parte dei casi, tuttavia, la is
parola chiave non è ancora ciò che desideri davvero, perché di solito non è sufficiente sapere che un oggetto è di un certo tipo. Di solito, si desidera effettivamente utilizzare quell'oggetto come istanza di quel tipo, il che richiede anche il cast. E così potresti ritrovarti a scrivere codice in questo modo:
if(o is Animal)
((Animal)o).Speak();
Ma ciò fa in modo che il CLR controlli il tipo di oggetto fino a due volte. Lo controllerà una volta per soddisfare l' is
operatore, e se o
è effettivamente un Animal
, lo faremo di nuovo per convalidare il cast.
È invece più efficiente farlo:
Animal a = o as Animal;
if(a != null)
a.Speak();
L' as
operatore è un cast che non genererà un'eccezione se fallisce, invece ritorna null
. In questo modo, il CLR controlla il tipo di oggetto solo una volta e, successivamente, dobbiamo solo fare un controllo nullo, che è più efficiente.
Ma attenzione: molte persone cadono in una trappola as
. Poiché non genera eccezioni, alcune persone lo considerano un cast "sicuro" e lo usano esclusivamente, evitando lanci regolari. Questo porta a errori come questo:
(o as Animal).Speak();
In questo caso, lo sviluppatore presuppone chiaramente che o
sarà sempre un Animal
, e fintanto che la sua ipotesi è corretta, tutto funziona bene. Ma se si sbagliano, allora quello che finiscono qui è a NullReferenceException
. Con un cast regolare, avrebbero ottenuto un InvalidCastException
invece, che avrebbe identificato più correttamente il problema.
A volte, questo bug può essere difficile da trovare:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
Questo è un altro caso in cui lo sviluppatore si aspetta chiaramente o
di esserlo Animal
ogni volta, ma questo non è ovvio nel costruttore, in cui as
viene utilizzato il cast. Non è ovvio finché non si arriva al Interact
metodo, dove animal
si prevede che il campo verrà assegnato in modo positivo. In questo caso, non solo si ottiene un'eccezione fuorviante, ma non viene generata fino a quando potenzialmente si è verificato molto più tardi di quando si è verificato l'errore effettivo.
In sintesi:
Se hai solo bisogno di sapere se un oggetto è di qualche tipo, usa is
.
Se devi trattare un oggetto come un'istanza di un certo tipo, ma non sai con certezza che l'oggetto sarà di quel tipo, usa as
e controlla null
.
Se devi trattare un oggetto come un'istanza di un certo tipo e si suppone che l'oggetto sia di quel tipo, usa un cast regolare.
as
!