GetType () kann lügen?


94

Basierend auf der folgenden Frage, die vor einigen Tagen in SO gestellt wurde: GetType () und Polymorphismus, und als ich Eric Lipperts Antwort las , begann ich zu überlegen, ob das GetType()Nicht-Virtuelle wirklich sicherstellen würde, dass ein Objekt nicht darüber lügen kann Type.

In Erics Antwort heißt es insbesondere:

Die Framework-Designer werden keine unglaublich gefährliche Funktion hinzufügen, z. B. das Zulassen, dass ein Objekt über seinen Typ lügt, nur um es mit drei anderen Methoden desselben Typs konsistent zu machen.

Die Frage ist nun: Kann ich ein Objekt erstellen, das über seinen Typ lügt, ohne dass es sofort offensichtlich ist? Ich kann mich hier zutiefst irren und würde gerne klarstellen, ob dies der Fall ist, aber beachten Sie den folgenden Code:

public interface IFoo
{
    Type GetType();
}

Und die folgenden zwei Implementierungen dieser Schnittstelle:

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

public class NiceFoo : IFoo
{
}

Wenn Sie dann das folgende einfache Programm ausführen:

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

Sicher genug badFoogibt eine fehlerhafte Ausgabe aus Type.

Jetzt weiß ich nicht, ob dies ernsthafte Auswirkungen hat, wenn Eric dieses Verhalten als " unglaublich gefährliches Merkmal " beschreibt, aber könnte dieses Muster eine glaubwürdige Bedrohung darstellen?


3
interessanter Titel und Thema!
David

43
IFoo.GetTypeund object.GetTypeist nicht dasselbe, also passiert hier nichts Schlimmes außer schlechtem Stil. Bearbeiten: GetTypeWird im Allgemeinen für ein Objekt aufgerufen, das zur Kompilierungszeit in den meisten Fällen nicht bekannt ist, objectund nicht für eine zweifelhafte Benutzeroberfläche. :)
Leppie

4
Ihr Titel, Sir, machte meinen Tag.
Soner Gönül

5
Sie stellen nur neue Mitglieder vor, die ebenfalls angerufen werden GetTypeund dieselbe Signatur haben. Das bezieht sich nicht auf die GetTypeMethode, die wichtig ist. Sie können auch eine öffentliche Instanzmethode erstellen, die die relevante GetTypeMethode mit dem newSchlüsselwort modifier verbirgt . Beachten Sie, dass wenn Sie eine generische Methode wie static Type Test<T>(T t) { return t.GetType(); }(ohne Einschränkung T) haben, Dinge wie Test<IFoo>(new BadFoo())die ursprüngliche GetTypeMethode immer noch aufrufen .
Jeppe Stig Nielsen

2
@Jamiec - Die Frage "Könnte dieses Muster eine glaubwürdige Bedrohung darstellen?" ist nicht rhetorisch.
Martin Smith

Antworten:


45

Gute Frage! So wie ich das sehe, könnte man einen Mitentwickler nur wirklich irreführen, wenn GetType auf einem Objekt virtuell wäre, was es nicht ist.

Was Sie getan haben, ähnelt dem Abschatten von GetType wie folgt:

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

Mit dieser Klasse (und unter Verwendung des Beispielcodes aus dem MSDN für die GetType () -Methode ) könnten Sie tatsächlich Folgendes haben:

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

Also, Huch, du hast erfolgreich gelogen, oder? Nun ja und nein ... Bedenken Sie, dass die Verwendung dieses Exploits die Verwendung Ihrer BadFoo-Instanz als Argument für eine Methode irgendwo bedeuten würde, die wahrscheinlich einen objectoder einen gemeinsamen Basistyp für eine Hierarchie von Objekten erwartet . Etwas wie das:

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)druckt aber "kein int".

Im Grunde genommen (zurück zu Ihrem Beispiel) können Sie Ihren "Lügentyp" also wirklich nur mit Code ausnutzen, den jemand gegen Ihre IFooSchnittstelle geschrieben hat, was sehr deutlich ist, dass es eine "benutzerdefinierte" GetType()Methode gibt.

Nur wenn GetType () auf einem Objekt virtuell wäre, könnten Sie einen "lügnerischen" Typ erstellen, der mit Methoden wie CheckIfIntoben verwendet werden könnte, um Chaos in Bibliotheken zu verursachen, die von jemand anderem geschrieben wurden.


Ja, es ist genau das gleiche wie Schatten. Der letzte Absatz macht wirklich deutlich, dass es wirklich keine Bedrohung gibt. Vielen Dank!
Zwischen

32

Es gibt zwei Möglichkeiten, sich über den Typ sicher zu sein:

  1. Verwenden Sie typeoffür den Typ, der nicht überladen werden kann

    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. Verwandeln Sie die Instanz in eine objectund rufen Sie die GetType()Methode auf

    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
Wie werden Sie typeofin einer Methode verwenden, die nur einen IFoo badFooas-Parameter erhält ?
Huysentruitw

typeofkann nicht auf Instanzen einer Klasse angewendet werden, was wir hier tun müssen. Ihre einzige Option ist GetType().
Zwischen

Ihre beiden Proben machen zwei verschiedene Dinge. Die zweiten Zeilen beantworten nicht die erforderliche Frage - sie "erhalten explizit den Typ BadFoo", aber sie "erhalten nicht den Typ der Variablen badFoo".
Dan Puzey

Ja, tut mir leid. Wie immer habe ich die Frage nicht sorgfältig genug gelesen. Ich habe meine Antwort aktualisiert, um die zwei verschiedenen Möglichkeiten aufzuzeigen, wie Sie sich über den Typ sicher sein können.
Johannes Wanzek

1
Darauf habe ich hingewiesen. Was ist der Sinn Ihres Kommentars? :)
Johannes Wanzek

10

Nein, Sie können GetType nicht zum Lügen bringen. Sie führen nur eine neue Methode ein. Nur Code, der diese Methode kennt, wird sie aufrufen.

Sie können beispielsweise nicht festlegen, dass Drittanbieter- oder Framework-Code Ihre neue GetType-Methode anstelle der realen Methode aufruft, da dieser Code nicht weiß, dass Ihre Methode vorhanden ist, und sie daher niemals aufruft.

Sie können jedoch Ihre eigenen Entwickler mit einer solchen Erklärung verwechseln. Jeder Code, der mit Ihrer Deklaration kompiliert wird und Parameter oder Variablen verwendet, die als IFoo oder ein davon abgeleiteter Typ eingegeben wurden, verwendet stattdessen Ihre neue Methode. Da dies jedoch nur Ihren eigenen Code betrifft, stellt es keine wirkliche "Bedrohung" dar.

Wenn Sie eine benutzerdefinierte Typbeschreibung für eine Klasse bereitstellen möchten, sollte dies mithilfe eines benutzerdefinierten Typdeskriptors erfolgen , indem Sie Ihre Klasse möglicherweise mit einem TypeDescriptionProviderAttribute versehen . Dies kann in einigen Situationen hilfreich sein.


2
+1 für Ihren zweiten Absatz, in dem ausdrücklich darauf hingewiesen wird, dass Code von Drittanbietern nichts über eine benutzerdefinierte GetType-Implementierung weiß. Andere Antworten deuteten auf diese Idee hin, kamen aber nicht wirklich heraus und sagten es (zumindest nicht so klar).
Brichins

7

Nun, tatsächlich gibt es bereits einen Typ, in dem liegen kann GetType: jeden nullbaren Typ.

Dieser Code :

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

Ausgänge True.


Eigentlich ist es nicht der, int?der lügt, sondern nur die implizite Besetzung, um objectsich int?in eine Box zu verwandeln int. Aber trotzdem kann man nichtint? von intmit erzählen GetType().


1
Welches Verhalten erwartet (oder zumindest bekannt) wird. Die Antworten auf diese Frage erklären dieses Konzept ziemlich klar sowie den Grund dafür.
Brichins

@brichins: Na ja, ich stimme es ist bekannt, aber ich kann zustimmen nicht , dass es gut -known. Auf jeden Fall ist dies ein Fall, in dem ein GetType()etwas seltsames Ergebnis erzielt wird. Ich habe tatsächlich mehrere Kollegen gefragt, ob ein nicht beschatteter GetType()Benutzer etwas zurückgeben kann, das sich vom Laufzeittyp des tatsächlichen Objekts unterscheidet. Alle antworteten mit "Nein".
Vlad

5

Ich glaube nicht, da jeder Bibliothekscode, der GetType aufruft, die Variable als 'Object' oder als generischen Typ 'T' deklariert.

Der folgende Code:

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

Drucke:

Objekt BadFoo sagt, er sei ein 'TypeConcept.BadFoo'

Objekt NiceFoo sagt, er sei ein 'TypeConcept.NiceFoo'

Generischer Typ BadFoo sagt, er sei ein 'TypeConcept.BadFoo'

Generischer Typ NiceFoo sagt, er sei ein 'TypeConcept.NiceFoo'

Das einzige Mal, dass diese Art von Code zu schlechten Szenarien führt, ist in Ihrem eigenen Code, in dem Sie den Parametertyp als IFoo deklarieren

    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 sagt, er sei ein 'System.Int32'

IFoo NiceFoo sagt, er sei ein 'TypeConcept.NiceFoo'


4

Das Schlimmste, was passieren kann, ist, irreführende unschuldige Programmierer, die zufällig die vergiftete Klasse benutzen, irrezuführen, zum Beispiel:

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

Wenn myInstancees sich um eine Instanz einer Klasse handelt, wie Sie sie in der Frage beschreiben, wird sie nur als unbekannter Typ behandelt.

Meine Antwort lautet also nein, ich kann hier keine wirkliche Bedrohung sehen.


1
Sicher. Ein sorgfältiger Programmierer kann zur Kompilierungszeit sehen, welche Methode "GetType" er aufruft. Object.GetType()unterscheidet sich von SomeUserdefinedInterfaceClassOrStruct.GetType(). Nur wenn Sie den dynamicTyp verwenden, können Sie nie wissen, was zur Bindungszeit passieren wird. Also sollten Sie dynamic x = expression; ... Type t = ((object)x).GetType();in solchen Fällen verwenden.
Jeppe Stig Nielsen

@ Jeppe faire Punkte! Ich denke, es rechtfertigt eine separate Antwort. Meine Antwort konzentriert sich mehr auf "unschuldige" Programmierer, die nicht so vorsichtig sind.
Schatten-Assistent ist Ohr für Sie

3

Sie haben einige Möglichkeiten, um gegen diese Art von Hack auf Nummer sicher zu gehen:

Zuerst zum Objekt werfen

Sie können die ursprüngliche GetType()Methode aufrufen , indem Sie die Instanz zuerst in Folgendes umwandeln object:

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

Ergebnisse in:

BadFoo says he's a 'ConsoleApplication.BadFoo'

Verwenden Sie die Vorlagenmethode

Wenn Sie diese Vorlagenmethode verwenden, erhalten Sie auch den tatsächlichen Typ:

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

GetType(badFoo);

2

Es gibt einen Unterschied zwischen object.GetTypeund IFoo.GetType. GetTypewird zur Kompilierungszeit für nicht bekannte Objekte und nicht für Schnittstellen aufgerufen. In Ihrem Beispiel wird bei der Ausgabe ein badFoo.GetTypeVerhalten erwartet, da Sie die Methode überladen. Das einzige ist, dass andere Programmierer von diesem Verhalten verwirrt werden können.

Wenn Sie es jedoch verwenden typeof(), wird ausgegeben, dass der Typ derselbe ist und Sie nicht überschreiben können typeof().

Außerdem kann der Programmierer zur Kompilierungszeit sehen, welche Methode GetTypeer aufruft.

Also zu Ihrer Frage: Dieses Muster kann keine glaubwürdige Bedrohung darstellen, ist aber auch nicht der beste Codierungsstil.


badFoo.GetType()IS erwartet Verhalten, weil GetTypeüberlastet wurde.
Huysentruitw
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.