유형 검사 : typeof, GetType 또는 is?


1512

많은 사람들이 다음 코드를 사용하는 것을 보았습니다.

Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

그러나 나는 당신도 이것을 할 수 있다는 것을 알고 있습니다 :

if (obj1.GetType() == typeof(int))
    // Some code here

아니면 이거:

if (obj1 is int)
    // Some code here

개인적으로, 나는 마지막 것이 가장 깨끗하다고 ​​생각하지만 누락 된 것이 있습니까? 어느 것이 가장 사용하기 좋습니까, 아니면 개인 취향입니까?


28
잊지 마세요 as!
RCIX

82
as실제로 타입 검사는 아니지만 ...
jasonh

49
as확실히 형식 검사의 한 형태입니다 is. 효과적으로 is배후에서 사용하며 MSDN의 코드 청결도를 향상시키는 곳에서 모든 곳에서 사용됩니다 is. is먼저 확인하는 대신 as사용할 준비가 된 형식화 된 변수 를 설정 하는 호출은 다음과 같습니다. null 인 경우 적절하게 응답합니다. 그렇지 않으면 계속하십시오. 확실히 내가 본 적이 있고 꽤 많이 사용했습니다.
Zaccone

15
귀하의 경우에 의미 론적 작업을 가정 할 때 as/ is( stackoverflow.com/a/27813381/477420 에서 다루는) 에 유리한 성능 차이가 있습니다.
Alexei Levenkov

@samusarin 그것은 "사용"반사를하지 않습니다. GetType당신이에 연결하는 방법에 System.Reflection.Assembly여기 완전히 다른 방법과 관련이없는 -.
Kirk Woll

답변:


1848

모두 다릅니다.

  • typeof 유형 이름 (컴파일시 지정)을 사용합니다.
  • GetType 인스턴스의 런타임 유형을 가져옵니다.
  • is 인스턴스가 상속 트리에 있으면 true를 반환합니다.

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

무엇에 대해 typeof(T)? 컴파일 타임에도 해결됩니까?

예. T는 항상 표현식의 유형입니다. 제네릭 메서드는 기본적으로 적절한 형식의 메서드입니다. 예:

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"

29
아, Car에서 파생 된 Ford 클래스와 Ford 인스턴스가 있다면 해당 인스턴스에서 "is Car"를 확인하는 것이 사실입니다. 맞는 말이다!
jasonh 2016 년

2
명확히하기 위해 알고 있었지만 코드 샘플을 추가하기 전에 주석을 달았습니다. 이미 훌륭한 답변에 명확한 영어 명확성을 추가하려고했습니다.
jasonh 2016 년

12
컴파일 시간에 typeof가 평가되고 런타임에 GetType ()이 평가되면 @Shimmy GetType ()에 약간의 성능 저하가 발생하는 것이 합리적입니다.
Cedric Mamo

new Dog (). GetType ()은 Animal입니까 아니면 typeof (Dog) is Animal이면 경고가 아닌 오류가 발생합니까?
Prerak K

7
@PrerakK의 new Dog().GetType() is Animal거짓 반환 (와 다른 버전뿐만 아니라) 이후 .GetType()반환 유형의 객체 Type, 그리고 Type하지 않습니다 Animal.
Maarten

195

컴파일 타임에typeof 유형을 가져 오려는 경우에 사용하십시오 . 실행시 유형을 가져 오려면 사용하십시오 . 캐스트와 같이 사용할 경우는 거의 없으며 대부분의 경우 변수를 캐스트합니다.GetTypeis

고려하지 않은 네 번째 옵션이 있습니다 (특히 찾은 유형으로 객체를 캐스트하려는 경우). 사용하는 것 as입니다.

Foo foo = obj as Foo;

if (foo != null)
    // your code here

이것은 하나의 캐스트 만 사용 하지만이 접근법은 다음과 같습니다.

if (obj is Foo)
    Foo foo = (Foo)obj;

가 필요합니다 .

업데이트 (2020 년 1 월) :

  • C # 7+ 부터는 이제 인라인 캐스트 할 수 있으므로 'is'접근법을 하나의 캐스트로 수행 할 수 있습니다.

예:

if(obj is Foo newLocalFoo)
{
    // For example, you can now reference 'newLocalFoo' in this local scope
    Console.WriteLine(newLocalFoo);
}

4
.NET 4의 변경 사항으로 is여전히 캐스트를 수행합니까?
ahsteele

6
이 답변이 맞습니까? 실제로 인스턴스를 typeof ()로 전달할 수 있다는 것이 사실입니까? 내 경험은 아니요. 그러나 인스턴스 확인은 런타임에 수행되어야하는 반면 일반적으로 클래스 확인은 컴파일 타임에 가능해야한다는 것이 사실이라고 생각합니다.
Jon Coombs

4
@jon (q. 4 년 후) 아니요, 인스턴스를 (으)로 전달할 수 없으며이 typeof()답변으로 할 수 있다고 제안하지 않습니다. 대신 유형, 즉 typeof(string)작동 typeof("foo")하지 않습니다.
Abel

나는 isIL에서 캐스트가 아닌 특별한 작업을 수행 한다고 생각하지 않습니다 .
abatishchev

3
우리는 지금 할 수 있습니다if (obj is Foo foo) { /* use foo here */ }
Ivan García Topete

71

1.

Type t = typeof(obj1);
if (t == typeof(int))

typeof변수가 아닌 유형에서만 작동 하기 때문에 이것은 불법 입니다. obj1이 변수라고 가정합니다. 따라서이 방법 typeof은 정적이며 런타임 대신 컴파일 타임에 작동합니다.

2.

if (obj1.GetType() == typeof(int))

이것이 정확히 유형 인 true경우 obj1입니다 int. 경우 obj1도출에서 int의 경우 조건이 될 것입니다 false.

삼.

if (obj1 is int)

이다 true경우 obj1이다 int, 또는 그것이라는 클래스에서 파생 된 경우 int, 또는 호출 인터페이스를 구현하는 경우 int.


1에 대해 생각하면 맞습니다. 그러나 여기 여러 코드 샘플에서 보았습니다. 유형 t = obj1.GetType ()이어야합니다.
jasonh 2016 년

4
그렇습니다. "typeof (obj1)"은 시도 할 때 컴파일되지 않습니다.
Scott Langham

4
그것은 선택 System.Int32 또는 C #에서 다른 값 유형에서 파생하는 것은 불가능
reggaeguitar

typeof (typeof (system.int32)) 무엇을 말할 수 있습니까
Sana

1
@Sana, 시도해 보지 않겠습니까 :) System.Type 유형을 나타내는 System.Type 인스턴스를 다시 가져 와서 상상해보십시오. typeof에 대한 설명서는 다음과 같습니다. docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
Scott Langham

53
Type t = typeof(obj1);
if (t == typeof(int))
    // Some code here

이것은 오류입니다. C #의 typeof 연산자는 개체가 아닌 형식 이름 만 사용할 수 있습니다.

if (obj1.GetType() == typeof(int))
    // Some code here

이것은 작동하지만 예상대로 작동하지 않을 수 있습니다. 값 유형의 경우 여기에 표시된 것처럼 허용되지만 참조 유형의 경우 유형이 상속 계층 구조의 다른 유형이 아닌 정확히 동일한 유형 인 경우에만 true를 리턴합니다 . 예를 들어 :

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

이 같이 인쇄 "o is something else"의 종류가 있기 때문에, o있다 Dog, 없다 Animal. 그러나 클래스 의 IsAssignableFrom메소드 를 사용하면이 작업을 수행 할 수 있습니다 Type.

if(typeof(Animal).IsAssignableFrom(o.GetType())) // note use of tested type
    Console.WriteLine("o is an animal");

그러나이 기술은 여전히 ​​큰 문제가되고 있습니다. 변수가 null 인 경우에 대한 호출 GetType()은 NullReferenceException을 발생시킵니다. 따라서 제대로 작동하려면 다음을 수행하십시오.

if(o != null && typeof(Animal).IsAssignableFrom(o.GetType()))
    Console.WriteLine("o is an animal");

이것으로 is키워드 와 동등한 동작을합니다 . 따라서 이것이 원하는 동작 인 경우 is보다 읽기 쉽고 효율적인 키워드를 사용해야합니다 .

if(o is Animal)
    Console.WriteLine("o is an animal");

그러나 대부분의 경우 is키워드는 여전히 원하는 것이 아닙니다. 일반적으로 객체가 특정 유형이라는 것을 아는 것만으로는 충분하지 않습니다. 일반적으로 실제로 해당 객체를 해당 유형의 인스턴스로 사용 하려면 캐스팅도 필요합니다. 따라서 다음과 같이 코드를 작성하는 것이 좋습니다.

if(o is Animal)
    ((Animal)o).Speak();

그러나 CLR은 객체 유형을 최대 두 번 확인합니다. is연산자 를 만족시키기 위해 한 번 확인하고 o실제로 인 경우 Animal캐스트를 다시 확인합니다.

대신이 작업을 수행하는 것이 더 효율적입니다.

Animal a = o as Animal;
if(a != null)
    a.Speak();

as운영자는 대신 반환, 실패 할 경우 예외를 throw하지 않습니다 캐스트입니다 null. 이런 식으로 CLR은 객체의 유형을 한 번만 확인한 다음 null 검사 만하면됩니다.

그러나 많은 사람들이와 함정에 빠지게됩니다 as. 예외를 던지지 않기 때문에 일부 사람들은 예외를 "안전한"캐스트로 생각하고 정기적으로 캐스트를 줄이면서 독점적으로 사용합니다. 다음과 같은 오류가 발생합니다.

(o as Animal).Speak();

이 경우, 개발자는 분명히 그 가정되는 o것입니다 항상Animal, 그리고 오래 자신의 가정이 올바른로, 모든 것이 잘 작동합니다. 그러나 그들이 틀렸다면, 여기에서 끝나는 것은입니다 NullReferenceException. 규칙적으로 캐스팅 InvalidCastException하면 문제를보다 정확하게 식별 할 수있게됩니다.

때로는이 버그를 찾기가 어려울 수 있습니다.

class Foo{
    readonly Animal animal;

    public Foo(object o){
        animal = o as Animal;
    }

    public void Interact(){
        animal.Speak();
    }
}

이것은 개발자가 명확하게 기대하고 또 다른 경우이다 oAnimal때마다, 그러나 이것은 생성자에 명확하지 않다 as캐스트가 사용됩니다. 필드가 긍정적으로 할당 될 것으로 예상되는 Interact방법에 도달 할 때까지 분명하지 않습니다 animal. 이 경우 오해의 소지가있을뿐 아니라 실제 오류가 발생했을 때보 다 훨씬 늦게 발생할 때까지 발생하지 않습니다.

요약하자면:

  • 객체가 어떤 유형인지 여부 만 알아야하는 경우을 사용하십시오 is.

  • 객체를 특정 유형의 인스턴스로 취급해야하지만 객체가 해당 유형인지 확실하지 않은 경우을 사용 as하고 확인하십시오 null.

  • 객체를 특정 유형의 인스턴스로 취급해야하고 객체가 해당 유형이어야하는 경우 규칙적인 캐스트를 사용하십시오.


if (o is Animal) ((Animal) o) .Speak ();이 문제는 무엇입니까? ? 자세한 내용을 알려주시겠습니까?
batmaci

2
@ batmaci : 정답입니다-두 가지 유형 검사가 발생합니다. 처음 인 o is Animal변수의 타입이 있는지 확인하기 위해 CLR을 요구하는 o이다 Animal. 두 번째 점검은 명령문에서 캐스트 될 때 ((Animal)o).Speak()입니다. 두 번 확인하지 않고을 사용하여 한 번 확인하십시오 as.
siride

분명히 설명해 주셔서 감사합니다.
Paul Efford

16

C # 7을 사용하고 있다면 Andrew Hare의 훌륭한 답변을 업데이트 할 때입니다. 패턴 일치 는 별도의 선언 / 캐스트 및 검사없이 if 문의 컨텍스트 내에서 형식화 된 변수를 제공하는 멋진 바로 가기를 도입했습니다.

if (obj1 is int integerValue)
{
    integerValue++;
}

이것은 이와 같은 단일 캐스트에 대해 매우 압도적이지만 루틴에 많은 유형이 올 때 실제로 빛납니다. 아래는 캐스팅을 두 번 피하는 오래된 방법입니다.

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

가능한 한이 코드를 축소하고 동일한 객체의 중복 캐스트를 피하는 것이 항상 귀찮았습니다. 위는 다음과 일치하는 패턴으로 압축되어 있습니다.

switch (obj1)
{
    case Button button:
        // do stuff...
        break;
    case TextBox text:
        // do stuff...
        break;
    case Label label:
        // do stuff...
        break;
    // and so on...
}

편집 : Palec의 의견에 따라 스위치를 사용하도록 더 긴 새로운 방법을 업데이트했습니다.


1
이 경우 switch패턴 일치와 함께 명령문을 사용 하는 것이 좋습니다.
Palec

당신은 어떻게 대처하지 않습니까? 이 특정 코드 블록에서? if (obj1 is int integerValue) { integerValue++; }
벤 Vertonghen

벤, 귀하의 질문을 이해하면 정수 변수에 정수가 아닌 정수를 넣을 수 없으므로 다른 경우를 처리하기 위해 else 문만 작성하십시오. :)
JoelC

14

나는 Type비교할 속성 이 있었고 is(처럼 my_type is _BaseTypetoLookFor) 사용할 수 없지만 다음을 사용할 수 있습니다.

base_type.IsInstanceOfType(derived_object);
base_type.IsAssignableFrom(derived_type);
derived_type.IsSubClassOf(base_type);

공지 사항 IsInstanceOfTypeIsAssignableFrom반환 true동일한 유형을 비교할 때, IsSubClassOf가 반환됩니다 false. 그리고 IsSubclassOf다른 두 사람이 할 인터페이스에서 작동하지 않습니다. ( 이 질문과 답변 도 참조하십시오 .)

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

9

내가 선호하는 IS를

즉, is를 사용하면 상속을 올바르게 사용 하지 않을 가능성이 높습니다 .

그 사람 : 실체, 그 동물 : 실체라고 가정하십시오. 피드는 Entity의 가상 메소드입니다 (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
    }
  }
}

차라리

class Person
{
  public override void Feed( Person p )
  {
    // feed the person
  }
  public override void Feed( Animal a )
  {
    // feed the animal
  }
}

1
사실, 나는 Person이 Animal에서 파생된다는 것을 알고는 전자를 절대로하지 않을 것입니다.
jasonh 2016 년

3
후자는 실제로 상속도 사용하지 않습니다. Foo는 사람과 동물로 재정의되는 Entity의 가상 방법이어야합니다.
Neil Williams

2
@bobobobo 나는 "상속"이 아니라 "과부하"를 의미한다고 생각합니다.
lc.

@lc : 아니요, 상속을 의미합니다. 첫 번째 예는 다른 동작을 얻는 잘못된 방법입니다 ( is 사용 ). 두 번째 예는 overloading yes를 사용하지만 is 사용을 피합니다 .
bobobobo 2016 년

1
예제의 문제점은 확장되지 않는다는 것입니다. 먹을 필요가있는 새로운 엔티티 (예 : 곤충 또는 몬스터)를 추가 한 경우 Entity 클래스에 새 메소드를 추가 한 후이를 제공하는 서브 클래스에서 대체해야합니다. 이것은 (엔티티가 X) 다른 경우 (엔티티가 Y) 인 경우 목록보다 더 바람직하지 않습니다 ... 이것은 LSP 및 OCP를 위반하므로 상속이 문제의 가장 좋은 해결책은 아닙니다. 어떤 형태의 위임이 선호 될 것입니다.
ebrown 2016 년

5

나는 마지막 것 또한 상속 (예 : Dog is Animal == true)을 보았으며, 이는 대부분의 경우 더 낫습니다.


2

내가하는 일에 달려 있습니다. 부울 값이 필요한 경우 (즉, int로 캐스팅할지 여부를 결정하기 위해)를 사용 is합니다. 실제로 어떤 이유로 든 유형이 필요한 경우 (예 : 다른 방법으로 전달)을 사용 GetType()합니다.


1
좋은 지적. if 문을 사용하여 유형을 확인한 몇 가지 답변을보고 난 후에이 질문에 도달했다는 것을 언급하지 않았습니다.
jasonh 2016 년

0

마지막은 더 깨끗하고 명확하며 하위 유형도 확인합니다. 다른 것은 다형성을 확인하지 않습니다.


0

형식에 대한 System.Type 개체를 얻는 데 사용됩니다. typeof 표현식은 다음 형식을 취합니다.

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
    */

이 샘플에서는 GetType 메서드를 사용하여 숫자 계산 결과를 포함하는 데 사용되는 형식을 결정합니다. 이는 결과 번호의 스토리지 요구 사항에 따라 다릅니다.

    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
    */

-4
if (c is UserControl) c.Enabled = enable;

4
자세한 내용으로 편집하십시오. 검색 가능한 콘텐츠가 포함되어 있지 않으며 누군가 "이 작업을 시도해야하는 이유"를 설명하지 않기 때문에 코드 전용 및 "이것을 시도하십시오"답변은 권장되지 않습니다.
abarisone

귀하의 답변은 질문과 관련이 없습니다.
menxin September

-5

C #에서 "typeof ()"연산자를 사용할 수 있지만 System.IO를 사용하여 네임 스페이스를 호출해야합니다. 유형을 확인하려면 "is"키워드를 사용해야합니다.


7
typeof네임 스페이스에 정의되어 있지 않으면 키워드입니다. System.IO이것과 아무 상관이 없습니다.
Arturo Torres Sánchez

-5

성능 테스트 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();
    }
}

디버그 모드 결과 :

00:00:08.4096636
00:00:10.8570657

릴리스 모드 결과 :

00:00:02.3799048
00:00:07.1797128

1
성능 측정을 위해 DateTime.UtcNow를 사용해서는 안됩니다. 코드와 함께 Stopwatch 클래스를 사용하면 디버그 모드와는 정반대의 결과가 나타납니다. UseTypeOf : 00 : 00 : 14.5074469 UseGetType : 00 : 00 : 10.5799534입니다. 릴리스 모드는 예상대로 동일
Alexey Shcherbak

@AlexeyShcherbak Stopwatch와 DateTime.Now의 차이는 10-20ms를 초과 할 수 없습니다. 코드를 다시 확인하십시오. 그리고 나는 테스트에서 밀리 초를 신경 쓰지 않습니다. 또한 내 코드는 스톱워치에서 더 긴 코드 줄입니다.
Alexander Vasilyev

1
특정한 경우가 아니라 일반적으로 나쁜 습관입니다.
Alexey Shcherbak

4
@AlexanderVasilyev 코드 라인의 양은 문서에 오해의 소지 가있는 것을 수행하는 인수로 사용되어서는 안됩니다 . msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx 에서 볼 수 있듯이 OS의 시간 프레임을 사용하므로 100msDateTime 미만의 시간이 걱정된다면 사용해서는 안됩니다 . 상대적으로 프로세서 "를 사용하는 , a로 사용되는 해상도 Win7에로는 백일해이 15ms이다. StopwatchTickDateTime
Eric Wu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.