GetType ()은 거짓말을 할 수 있습니까?


94

며칠 전 SO : GetType () 및 다형성Eric Lippert의 답변을 읽은 다음 질문을 기반으로 GetType()가상이 아닌 것이 실제로 객체가 Type.

특히 Eric의 대답은 다음과 같습니다.

프레임 워크 디자이너는 동일한 유형의 다른 세 가지 메서드와 일관성을 유지하기 위해 객체가 해당 유형에 대해 거짓말을하도록 허용하는 등 엄청나게 위험한 기능을 추가하지 않을 것입니다.

이제 질문은 : 나는 개체 수 않습니다 즉시 분명하지 않고 그 형태에 대해 거짓말을? 나는 여기서 완전히 틀렸을 수 있으며 그럴 경우 명확하게 설명하고 싶지만 다음 코드를 고려하십시오.

public interface IFoo
{
    Type GetType();
}

그리고 상기 인터페이스의 다음 두 가지 구현 :

public class BadFoo : IFoo
{
    Type IFoo.GetType()
    {
        return typeof(int);
    }
}

public class NiceFoo : IFoo
{
}

그런 다음 다음과 같은 간단한 프로그램을 실행하면 :

static void Main(string[] args)
{
    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    Console.ReadLine();
}

확실히 충분히 badFoo잘못된 Type.

이제이 행동이 " 매우 위험한 기능 " 이라고 설명하는 Eric을 기반으로 한 심각한 의미가 있는지 모르겠지만 이 패턴이 신뢰할 수있는 위협이 될 수 있습니까?


3
흥미로운 제목과 주제!
David

43
IFoo.GetType그리고 object.GetType똑같은 것이 아니기 때문에 나쁜 스타일을 제외하고는 여기서 나쁜 일이 일어나지 않습니다. 편집 : 일반적으로 GetType컴파일 타임에 알려지지 않은 일부 개체에서 호출됩니다. 대부분의 경우 object일부 이상한 인터페이스가 아닙니다. :)
leppie 2013 년

4
당신의 직함은 내 하루를 만들었습니다.
Soner Gönül 2013 년

5
동일한 서명을 사용하여 라는 회원을 소개 GetType합니다. 그것은 GetType중요한 방법 과 관련이 없습니다 . modifier 키워드를 GetType사용하여 관련 메서드 를 숨기는 공용 인스턴스 메서드를 만들 수도 있습니다 new. static Type Test<T>(T t) { return t.GetType(); }(에 대한 제약없이 T) 와 같은 일반 메서드가있는 경우 같은 항목 Test<IFoo>(new BadFoo())은 여전히 ​​원래 GetType메서드를 호출합니다 .
Jeppe Stig Nielsen

2
@Jamiec-질문 "이 패턴이 신뢰할 수있는 위협이 될 수 있습니까?" 수사적이지 않습니다.
Martin Smith

답변:


45

좋은 질문! 내가보기에 GetType이 가상 객체 인 경우에만 동료 개발자를 오도 할 수 있지만 그렇지 않습니다.

당신이 한 일은 다음과 같이 GetType을 섀도 잉하는 것과 유사합니다.

public class BadFoo
{
    public new Type GetType()
    {
        return typeof(int);
    }
}

이 클래스 (및 GetType () 메서드에 대한 MSDN의 샘플 코드 사용 )를 사용하면 실제로 다음을 수행 할 수 있습니다.

int n1 = 12;
BadFoo foo = new BadFoo();

Console.WriteLine("n1 and n2 are the same type: {0}",
                  Object.ReferenceEquals(n1.GetType(), foo.GetType())); 
// output: 
// n1 and n2 are the same type: True

그래서, yikes, 당신은 성공적으로 거짓말을했습니다. 음, 예와 아니오 ... 이것을 익스플로잇으로 사용하는 것은 BadFoo 인스턴스를 어딘가의 메서드에 대한 인수로 사용하는 것을 의미하며, 이는 object개체 계층 구조에 대한 공통 기본 유형을 예상 합니다. 이 같은:

public void CheckIfInt(object ob)
{
    if(ob.GetType() == typeof(int))
    {
        Console.WriteLine("got an int! Initiate destruction of Universe!");
    }
    else
    {
        Console.WriteLine("not an int");
    }
}

그러나 CheckIfInt(foo)"not an int"를 인쇄합니다.

따라서 기본적으로 (귀하의 예제로 돌아가서) 누군가가 IFoo인터페이스 에 대해 작성한 코드로만 "거짓 유형"을 악용 할 수 있습니다. 이는 "사용자 지정" GetType()메서드 가 있다는 사실에 대해 매우 분명합니다 .

GetType ()이 객체에서 가상 인 경우에만 다음과 같은 메서드와 함께 사용할 수있는 "거짓말"유형을 만들 수 있습니다. CheckIfInt 위와 다른 사람이 작성한 라이브러리에 혼란 .


네, 그림자와 똑같습니다. 마지막 단락은 실제로 위협이 없다는 것을 분명히하는 것입니다. 감사!
InBetween

32

유형에 대해 확신하는 방법에는 두 가지가 있습니다.

  1. typeof과부하가 걸리지 않는 Type에 사용

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", typeof(BadFoo));
    Console.WriteLine("NiceFoo really is a '{0}'", typeof(NiceFoo));
    Console.ReadLine();
  2. 인스턴스를로 캐스팅하고 메서드를 object호출합니다.GetType()

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", ((object)badFoo).GetType());
    Console.WriteLine("NiceFoo really is a '{0}'", ((object)niceFoo).GetType());
    Console.ReadLine();

1
as 매개 변수 typeof만 가져 오는 메소드에서 어떻게 사용 IFoo badFoo합니까?
huysentruitw

typeof여기서해야하는 클래스의 인스턴스에는 적용 할 수 없습니다. 유일한 옵션은 GetType()입니다.
InBetween

두 샘플은 두 가지 다른 작업을 수행합니다. 두 번째 줄은 필요한 질문에 답하지 않습니다. 명시 적으로 "유형을 얻지 BadFoo만" "변수 유형을 얻지"않습니다 badFoo.
Dan Puzey 2013 년

네, 미안 해요. 평소처럼 나는 질문을 충분히주의 깊게 읽지 않았습니다. 나는 유형에 대해 확신하는 두 가지 다른 방법을 지적하기 위해 내 대답을 업데이트했습니다.
Johannes Wanzek 2013 년

1
이것이 제가 지적한 것입니다. 그래서 당신의 의견의 요점은 무엇입니까? :)
Johannes Wanzek 2013 년

10

아니요, GetType을 거짓말로 만들 수 없습니다. 새로운 방법을 소개하는 것뿐입니다. 이 메서드를 인식하는 코드 만이 메서드를 호출합니다.

예를 들어 타사 또는 프레임 워크 코드에서 실제 메서드 대신 새 GetType 메서드를 호출하도록 만들 수 없습니다. 해당 코드는 메서드가 존재한다는 사실을 모르기 때문에 절대 호출하지 않기 때문입니다.

그러나 자신의 개발자를 그러한 선언과 혼동 할 수 있습니다. 선언으로 컴파일되고 IFoo로 입력 된 매개 변수 또는 변수를 사용하는 코드 또는 그로부터 파생 된 모든 유형은 실제로 새 메서드를 대신 사용합니다. 그러나 그것은 자신의 코드에만 영향을 미치기 때문에 실제로 "위협"을 부과하지는 않습니다.

클래스에 대한 사용자 지정 형식 설명을 제공하려면 TypeDescriptionProviderAttribute로 클래스에 주석을 추가하여 Custom Type Descriptor를 사용하여 수행해야합니다 . 이것은 어떤 상황에서 유용 할 수 있습니다.


2
제 3 자 코드가 사용자 지정 GetType 구현에 대해 알지 못함을 명시 적으로 지적하는 두 번째 단락에 +1. 다른 답변은 그 아이디어를 암시했지만 실제로 나오지 않고 말하지 않았습니다 (적어도 명확하지는 않습니다).
brichins 2013 년

7

글쎄요, 실제로 GetType 어떤 nullable 타입이든 속할 수있는 타입 이미 있습니다 .

이 코드 :

int? x = 0; int y = 0;
Console.WriteLine(x.GetType() == y.GetType());

출력 True.


사실, 그것은 아닙니다 int?에 거짓말을하고, 단지 암시 적 캐스트 object회전 int?박스형으로 int. 그러나 그럼에도 불구하고 당신이 할 수없는int?에서 intGetType().


1
예상되는 (또는 적어도 잘 알려진) 동작입니다. 이 질문에 대한 답변은 개념과 그 이유를 매우 명확하게 설명합니다.
brichins 2013 년

@brichins : 글쎄, 나는 그것이 알려진 것에 동의하지만 그것이 알려진 것에 동의 할 수 없습니다 . 어쨌든 이것은 GetType()다소 이상한 결과 가 나오는 경우 입니다. 실제로 여러 동료에게 그림자 GetType()가 없는 것이 실제 객체의 런타임 유형과 다른 것을 반환 할 수 있는지 물어 봤는데 모든 사람의 대답은 '아니오'였습니다.
Vlad

5

GetType을 호출하는 모든 라이브러리 코드는 변수를 'Object'또는 Generic 유형 'T'로 선언하기 때문에 그렇게 될 것이라고 생각하지 않습니다.

다음 코드 :

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintObjectType("BadFoo", badFoo);
        PrintObjectType("NiceFoo", niceFoo);
        PrintGenericType("BadFoo", badFoo);
        PrintGenericType("NiceFoo", niceFoo);
    }

    public static void PrintObjectType(string actualName, object instance)
    {
        Console.WriteLine("Object {0} says he's a '{1}'", actualName, instance.GetType());
    }

    public static void PrintGenericType<T>(string actualName, T instance)
    {
        Console.WriteLine("Generic Type {0} says he's a '{1}'", actualName, instance.GetType());
    }

인쇄물:

Object BadFoo는 그가 'TypeConcept.BadFoo'라고 말합니다.

개체 NiceFoo는 그가 'TypeConcept.NiceFoo'라고 말합니다.

Generic Type BadFoo는 그가 'TypeConcept.BadFoo'라고 말합니다.

Generic Type NiceFoo는 그가 'TypeConcept.NiceFoo'라고 말합니다.

이런 종류의 코드로 인해 잘못된 시나리오가 발생하는 유일한 경우는 매개 변수 유형을 IFoo로 선언하는 자체 코드에 있습니다.

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintIFoo("BadFoo", badFoo);
        PrintIFoo("NiceFoo", niceFoo);
    }

    public static void PrintIFoo(string actualName, IFoo instance)
    {
        Console.WriteLine("IFoo {0} says he's a '{1}'", actualName, instance.GetType());
    }

IFoo BadFoo는 그가 'System.Int32'라고 말합니다.

IFoo NiceFoo는 그가 'TypeConcept.NiceFoo'라고 말합니다.


4

내가 말할 수있는 한 최악의 상황은 감염된 클래스를 사용하는 무고한 프로그래머를 오도하는 것입니다. 예를 들면 다음과 같습니다.

Type type = myInstance.GetType();
string fullName = type.FullName;
string output;
if (fullName.Contains(".Web"))
{
    output = "this is webby";
}
else if (fullName.Contains(".Customer"))
{
    output = "this is customer related class";
}
else
{
    output = "unknown class";
}

myInstance질문에서 설명하는 것과 같은 클래스의 인스턴스 인 경우 알 수없는 유형으로 처리됩니다.

그래서 제 대답은 '아니오'입니다. 여기서 진짜 위협을 볼 수 없습니다.


1
확실한. 신중한 프로그래머는 자신이 호출하는 "GetType"메소드를 컴파일 타임에 볼 수 있습니다. Object.GetType()와는 다릅니다 SomeUserdefinedInterfaceClassOrStruct.GetType(). dynamic유형 을 사용하는 경우에만 바인딩시 어떤 일이 발생할지 알 수 없습니다. 따라서 그런 경우에 사용해야 dynamic x = expression; ... Type t = ((object)x).GetType();합니다.
Jeppe Stig Nielsen

@Jeppe 공정한 포인트! 나는 그것이 별도의 대답을 정당화한다고 생각하며, 내 대답은 그렇게 조심하지 않을 "순진한"프로그래머에 더 중점을 둡니다.
그림자 마법사가 당신을 위해 귀입니다

3

이러한 종류의 해킹에 대해 안전하게 플레이하려면 몇 가지 옵션이 있습니다.

먼저 개체에 캐스팅

GetType()먼저 인스턴스를 다음으로 캐스팅 하여 원래 메서드를 호출 할 수 있습니다 object.

 Console.WriteLine("BadFoo says he's a '{0}'", ((object)badFoo).GetType());

결과 :

BadFoo says he's a 'ConsoleApplication.BadFoo'

템플릿 방법 사용

이 템플릿 방법을 사용하면 실제 유형도 제공됩니다.

static Type GetType<T>(T obj)
{
    return obj.GetType();
}

GetType(badFoo);

2

object.GetType와 사이에 차이가 IFoo.GetType있습니다. GetType인터페이스가 아닌 알 수없는 개체에서 컴파일 타임에 호출됩니다. 귀하의 예에서, 출력badFoo.GetType 을 사용하면 메서드를 오버로드하기 때문에 bahaviour가 예상됩니다. 유일한 것은 다른 프로그래머가이 동작을 혼동 할 수 있다는 것입니다.

그러나 사용 typeof()하면 유형이 동일하다는 것을 출력하고 덮어 쓸 수 없습니다.typeof() .

또한 프로그래머는 컴파일 시간에 어떤 방법을 GetType 그가 호출 를 .

따라서 귀하의 질문에이 패턴은 신뢰할 수있는 위협이 될 수는 없지만 최상의 코딩 스타일은 아닙니다.


badFoo.GetType()GetType과부하로 인해 예상되는 동작 입니다.
huysentruitw
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.