[c#] GetType ()은 거짓말을 할 수 있습니까?

며칠 전 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을 기반으로 한 심각한 의미가 있는지 모르겠지만 이 패턴이 신뢰할 수있는 위협이 될 수 있습니까?



답변

좋은 질문! 내가보기에 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 위와 다른 사람이 작성한 라이브러리에 혼란 .


답변

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

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

답변

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

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

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

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


답변

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

이 코드 :

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

출력 True.


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


답변

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’라고 말합니다.


답변

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

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질문에서 설명하는 것과 같은 클래스의 인스턴스 인 경우 알 수없는 유형으로 처리됩니다.

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


답변

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

먼저 개체에 캐스팅

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