Sprawdzanie typu: typeof, GetType, czy jest?
Widziałem wiele osób używających następującego kodu:
Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
Ale wiem, że ty też możesz to zrobić:
if (obj1.GetType() == typeof(int))
// Some code here
Lub to:
if (obj1 is int)
// Some code here
Osobiście uważam, że ostatni jest najczystszy, ale czy czegoś mi brakuje? Który z nich jest najlepszy w użyciu, czy są to osobiste preferencje? 14 answers
Wszystkie są inne.
-
typeof
pobiera nazwę typu (którą określasz podczas kompilacji). -
GetType
pobiera Typ runtime instancji. -
is
zwraca true, jeśli instancja znajduje się w drzewie dziedziczenia.
Przykład
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);
Tak. T jest zawsze tym, jaki jest typ wyrażenia. Pamiętaj, że metoda generyczna to w zasadzie cała masa metod z odpowiedni typ. Przykład:A co z
typeof(T)
? Czy jest on również rozwiązany w czasie kompilacji?
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"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-07 03:22:47
Użyj typeof
, Gdy chcesz uzyskać typ w czasie kompilacji . Użyj GetType
, Gdy chcesz uzyskać typ w Czas wykonania . Rzadko zdarzają się przypadki użycia is
, ponieważ wykonuje ona rzut, a w większości przypadków i tak kończy się rzutem zmiennej.
Jest czwarta opcja, której nie rozważałeś (zwłaszcza jeśli zamierzasz oddać obiekt do typu, który również znajdziesz); Jest to użycie as
.
Foo foo = obj as Foo;
if (foo != null)
// your code here
Używa się tylko jednej obsady podejście to:
if (obj is Foo)
Foo foo = (Foo)obj;
Wymaga dwóch.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-22 10:16:03
1.
Type t = typeof(obj1);
if (t == typeof(int))
Jest to nielegalne, ponieważ typeof działa tylko na typach, a nie na zmiennych. Zakładam, że obj1 jest zmienną. W ten sposób typeof jest statyczny i wykonuje swoją pracę w czasie kompilacji zamiast w trybie runtime.
2.
if (obj1.GetType() == typeof(int))
To prawda, jeśli obj1 jest dokładnie typu int. Jeśli obj1 wywodzi się z int, warunek if będzie false.
3.
if (obj1 is int)
Jest to prawda, jeśli obj1 jest int, lub jeśli wywodzi się z klasy o nazwie int, lub jeśli implementuje interfejs o nazwie int.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:17:26
Type t = typeof(obj1);
if (t == typeof(int))
// Some code here
To jest błąd. Operator typeof w C# może przyjmować tylko nazwy typów, a nie obiektów.
if (obj1.GetType() == typeof(int))
// Some code here
To zadziała, ale może nie tak, jak byś się spodziewał. Dla typów wartości, jak pokazałeś tutaj, jest to dopuszczalne, ale dla typów referencyjnych zwracałoby to true tylko wtedy, gdy typ jest typem dokładnie takim samym, a nie czymś innym w hierarchii dziedziczenia. Na przykład:
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");
}
To wydrukuje "o is something else"
, ponieważ typem o
jest Dog
, a nie Animal
. Możesz zrób to jednak, jeśli użyjesz metody IsAssignableFrom
klasy Type
.
if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
Console.WriteLine("o is an animal");
[38]}ta technika wciąż pozostawia jednak poważny problem. Jeśli zmienna ma wartość null, wywołanie GetType()
spowoduje wywołanie NullReferenceException. Tak więc, aby to działało poprawnie, wykonałbyś:
if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
Console.WriteLine("o is an animal");
Z tym, masz równoważne zachowanie słowa kluczowego is
. Dlatego, jeśli takie zachowanie chcesz, powinieneś użyć słowa kluczowego is
, które jest bardziej czytelne i wydajniejsze.
if(o is Animal)
Console.WriteLine("o is an animal");
W w większości przypadków słowo kluczowe is
nadal nie jest tym, czego naprawdę chcesz, ponieważ zwykle nie wystarczy wiedzieć, że obiekt jest określonego typu. Zazwyczaj, chcesz rzeczywiście używać tego obiektu jako instancji tego typu, co wymaga również odlewania go. I tak możesz znaleźć się pisząc kod w ten sposób:
if(o is Animal)
((Animal)o).Speak();
Ale to sprawia, że CLR sprawdza typ obiektu nawet dwa razy. Sprawdzi go raz, aby spełnić operator is
, a jeśli o
jest rzeczywiście Animal
, sprawdzamy ponownie, aby potwierdzić Obsada.
Animal a = o as Animal;
if(a != null)
a.Speak();
Operator as
jest obsadą, która nie wyrzuci wyjątku, jeśli się nie powiedzie, zamiast tego zwróci null
. W ten sposób CLR sprawdza typ obiektu tylko raz, a po tym, po prostu musimy zrobić sprawdzenie null, co jest bardziej efektywne.
Ale uważaj: wiele osób wpada w pułapkę z as
. Ponieważ nie rzuca WYJĄTKÓW, niektórzy uważają go za " Bezpieczny" rzucają i używają go wyłącznie, unikając regularnych odlewów. Prowadzi to do błędów takich jak:
(o as Animal).Speak();
W tym przypadku deweloper zakłada, że o
będzie zawsze będzie Animal
i tak długo, jak ich założenie jest poprawne, wszystko działa dobrze. Ale jeśli się mylą, to kończą z NullReferenceException
. Przy regularnej obsadzie otrzymaliby zamiast tego InvalidCastException
, który lepiej zidentyfikowałby problem.
Czasami ten błąd może być trudno znaleźć:
class Foo{
readonly Animal animal;
public Foo(object o){
animal = o as Animal;
}
public void Interact(){
animal.Speak();
}
}
Jest to kolejny przypadek, w którym deweloper wyraźnie spodziewa się, że o
będzie Animal
za każdym razem, ale nie jest to oczywiste w konstruktorze, w którym używany jest as
cast. Nie jest to oczywiste, dopóki nie dojdziesz do metody Interact
, gdzie oczekuje się, że pole animal
będzie dodatnio przypisane. W tym przypadku nie tylko kończysz z wprowadzającym w błąd wyjątkiem, ale zostanie on wyrzucony dopiero potencjalnie znacznie później niż w momencie wystąpienia rzeczywistego błędu.
W podsumowanie:
Jeśli chcesz tylko wiedzieć, czy obiekt jest jakiegoś typu, użyj
is
.Jeśli chcesz traktować obiekt jako instancję określonego typu, ale nie masz pewności, czy obiekt będzie tego typu, użyj
as
i sprawdź, czy nie manull
.Jeśli chcesz traktować obiekt jako instancję określonego typu, A obiekt ma być tego typu, użyj zwykłego odlewu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-11 17:02:47
Miałem Type
-właściwość do porównania i nie mogłem użyć is
(Jak my_type is _BaseTypetoLookFor
), ale mogłem użyć tych:
base_type.IsInstanceOfType(derived_object);
base_type.IsAssignableFrom(derived_type);
derived_type.IsSubClassOf(base_type);
Zauważ, że IsInstanceOfType
i IsAssignableFrom
zwracają true
podczas porównywania tych samych typów, gdzie IsSubClassOf zwróci false
. I IsSubclassOf
nie działa na interfejsach, gdzie dwa pozostałe działają. (Zobacz także to pytanie i odpowiedź .)
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37
Jeśli używasz C # 7, to czas na aktualizację świetnej odpowiedzi Andrew Hare ' a. Pattern matching wprowadził ładny skrót, który daje nam wpisaną zmienną w kontekście instrukcji if, bez konieczności oddzielnej deklaracji / cast i check:
if (obj1 is int integerValue)
{
integerValue++;
}
Wygląda to dość rozczarowująco jak na taką pojedynczą obsadę, ale naprawdę świeci, gdy masz wiele możliwych typów w swojej rutynie. Poniżej znajduje się stary sposób na uniknięcie rzucania dwukrotnie:
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
Praca nad zmniejszeniem tego kodu w jak największym stopniu, jak również unikanie duplikatów odlewów tego samego obiektu zawsze mi przeszkadzało. Powyższe jest ładnie skompresowane z wzorcem pasującym do następującego:
switch (obj1)
{
case Button button:
// do stuff...
break;
case TextBox text:
// do stuff...
break;
case Label label:
// do stuff...
break;
// and so on...
}
EDIT: Zaktualizowano dłuższą nową metodę, aby użyć przełącznika zgodnie z komentarzem Palec.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 19:56:38
Wolę to
To powiedziawszy, jeśli używasz to , prawdopodobnie nie używasz dziedziczenia prawidłowo.
Załóżmy, że osoba: byt, i że zwierzę: byt. Feed jest wirtualną metodą w Entity (aby uczynić Neila szczęśliwym)
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
}
}
}
Raczej
class Person
{
public override void Feed( Person p )
{
// feed the person
}
public override void Feed( Animal a )
{
// feed the animal
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-23 14:10:23
Wydaje mi się, że ten ostatni dotyczy również dziedziczenia (np. pies to zwierzę == prawda), co w większości przypadków jest lepsze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:14:00
To zależy od tego, co robię. Jeśli potrzebuję wartości bool (powiedzmy, aby określić, czy będę rzucać do int), użyję is
. Jeśli rzeczywiście potrzebuję tego typu z jakiegoś powodu (powiedzmy, aby przejść do innej metody) użyję GetType()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:20:16
Ostatni jest czystszy, bardziej oczywisty, a także sprawdza podtypy. Pozostałe nie sprawdzają polimorfizmu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-11 19:16:12
Używane do uzyskania systemu.Obiekt typu typ. Wyrażenie typeof przyjmuje następującą postać:
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
*/
Ta próbka używa metody GetType do określenia typu, który ma zawierać wynik obliczeń numerycznych. Zależy to od wymagań przechowywania wynikowego numeru.
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
*/
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-14 07:52:21
if (c is UserControl) c.Enabled = enable;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-06 13:18:46
Możesz użyć operatora "typeof ()" w C#, ale musisz wywołać przestrzeń nazw używając System.IO; musisz użyć słowa kluczowego "is", jeśli chcesz sprawdzić typ.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-07 07:01:41
Test wydajności 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();
}
}
Wyniki w trybie debugowania:
00:00:08.4096636
00:00:10.8570657
Wyniki w trybie release:
00:00:02.3799048
00:00:07.1797128
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-03 13:58:53