GetType () peut mentir?


94

En me basant sur la question suivante posée il y a quelques jours dans SO: GetType () et le polymorphisme et en lisant la réponse d' Eric Lippert , j'ai commencé à GetType()me demander si le fait de ne pas être virtuel assurait vraiment qu'un objet ne pouvait pas mentir à son sujet Type.

Plus précisément, la réponse d'Eric déclare ce qui suit:

Les concepteurs de framework ne vont pas ajouter une fonctionnalité incroyablement dangereuse telle que permettre à un objet de mentir sur son type simplement pour le rendre cohérent avec trois autres méthodes sur le même type.

Maintenant , la question est: puis - je faire un objet qui ne mentir sur son type sans qu'il soit immédiatement évident? Je me trompe peut-être profondément et j'aimerais avoir des éclaircissements si tel est le cas, mais considérez le code suivant:

public interface IFoo
{
    Type GetType();
}

Et les deux implémentations suivantes de ladite interface:

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

public class NiceFoo : IFoo
{
}

Ensuite, si vous exécutez le programme simple suivant:

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

Bien sûr, badFoogénère une erreur Type.

Maintenant, je ne sais pas si cela a des implications sérieuses basées sur Eric décrivant ce comportement comme une " caractéristique incroyablement dangereuse ", mais ce modèle pourrait-il constituer une menace crédible?


3
titre et sujet intéressants!
David

43
IFoo.GetTypeet ce object.GetTypen'est pas la même chose donc rien de mal ne se passe ici sauf un style médiocre. Edit: Généralement, GetTypeil sera appelé sur un objet inconnu au moment de la compilation, dans la plupart des cas objectet non sur une interface douteuse. :)
leppie

4
Votre titre monsieur, a fait ma journée.
Soner Gönül

5
Vous venez de présenter de nouveaux membres également appelés GetType, avec la même signature. Cela ne concerne pas la GetTypeméthode qui est importante. Vous pouvez également créer une méthode d'instance publique qui masque la GetTypeméthode appropriée , à l'aide du newmot-clé modifier. Notez que si vous avez une méthode générique comme static Type Test<T>(T t) { return t.GetType(); }(sans contrainte T), alors des trucs comme Test<IFoo>(new BadFoo())appelleront toujours la GetTypeméthode d' origine .
Jeppe Stig Nielsen

2
@Jamiec - La question "ce modèle pourrait-il constituer une menace crédible?" n'est pas rhétorique.
Martin Smith

Réponses:


45

Bonne question! À mon avis, vous ne pourriez vraiment tromper un autre développeur que si GetType était virtuel sur un objet, ce qui n'est pas le cas.

Ce que vous avez fait s'apparente à l'observation de GetType, comme ceci:

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

avec cette classe (et en utilisant l'exemple de code du MSDN pour la méthode GetType () ), vous pourriez en effet avoir:

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

alors, yikes, vous avez menti avec succès, non? Eh bien, oui et non ... Considérez que l'utiliser comme un exploit signifierait utiliser votre instance BadFoo comme argument d'une méthode quelque part, qui attend probablement un objectou un type de base commun pour une hiérarchie d'objets. Quelque chose comme ça:

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

mais CheckIfInt(foo)affiche "pas un int".

Donc, en gros (pour revenir à votre exemple), vous ne pourriez vraiment exploiter votre "type couché" qu'avec du code que quelqu'un a écrit contre votre IFoointerface, ce qui est très explicite sur le fait qu'il a une GetType()méthode "personnalisée" .

Seulement si GetType () était virtuel sur un objet, vous seriez capable de créer un type "couché" qui pourrait être utilisé avec des méthodes comme CheckIfIntci-dessus pour créer des ravages dans les bibliothèques écrites par quelqu'un d'autre.


ouais, c'est exactement la même chose que l'observation. Le dernier paragraphe est ce qui montre vraiment qu'il n'y a vraiment aucune menace. Merci!
Entre

32

Il y a deux façons d'être sûr du type:

  1. Utiliser typeofsur le type qui ne peut pas être surchargé

    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. Cast l'instance en an objectet appeler la GetType()méthode

    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
Comment allez-vous l'utiliser typeofdans une méthode qui n'obtient qu'un IFoo badFooparamètre as?
huysentruitw

typeofne peut pas être appliqué aux instances d'une classe, ce que nous devons faire ici. Votre seule option est GetType().
Entre

Vos deux échantillons font deux choses différentes. Les deuxièmes lignes ne répondent pas à la question requise - elles «obtiennent explicitement le type BadFoo» mais elles n'obtiennent pas le type de variable badFoo.
Dan Puzey

Ouais je suis désolé. Comme d'habitude, je n'ai pas lu la question assez attentivement. J'ai mis à jour ma réponse pour souligner les deux façons différentes d'être sûr du type.
Johannes Wanzek

1
C'est ce que je signalais. Alors, quel est le point de votre commentaire? :)
Johannes Wanzek

10

Non, vous ne pouvez pas faire mentir GetType. Vous introduisez seulement une nouvelle méthode. Seul le code qui connaît cette méthode l'appellera.

Vous ne pouvez pas, par exemple, faire en sorte que le code tiers ou cadre appelle votre nouvelle méthode GetType au lieu de la vraie, car ce code ne sait pas que votre méthode existe et ne l'appellera donc jamais.

Vous pouvez cependant confondre vos propres développeurs avec une telle déclaration. Tout code compilé avec votre déclaration et utilisant des paramètres ou des variables typés IFoo ou tout type dérivé de celui-ci utilisera en effet votre nouvelle méthode à la place. Mais comme cela n'affecte que votre propre code, cela n'impose pas vraiment de «menace».

Si vous souhaitez fournir une description de type personnalisée pour une classe, cela doit être fait à l'aide d'un descripteur de type personnalisé , peut-être en annotant votre classe avec un TypeDescriptionProviderAttribute . Cela peut être utile dans certaines situations.


2
+1 pour votre deuxième paragraphe soulignant explicitement que le code tiers ne connaît pas une implémentation GetType personnalisée. D'autres réponses ont fait allusion à cette idée mais ne sont pas vraiment sorties et l'ont dit (du moins pas aussi clairement).
brichins

7

Eh bien, en fait, il existe déjà un type qui peut résider dans GetType: tout type Nullable.

Ce code :

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

sorties True.


En fait, ce n'est pas celui int?qui ment, juste un casting implicite pour objectse transformer int?en boîte int. Mais néanmoins vous ne pouvez pas dire int?de intavec GetType().


1
Quel est le comportement attendu (ou du moins bien connu). Les réponses à cette question expliquent assez clairement ce concept, ainsi que sa raison.
brichins

@brichins: Eh bien, je suis d'accord que c'est connu, mais je ne peux pas accepter que ce soit bien connu. Quoi qu'il en soit, c'est un cas où GetType()produit un résultat quelque peu étrange. J'ai en fait demandé à plusieurs collègues si un non-shadow GetType()peut renvoyer quelque chose qui diffère du type d'exécution de l'objet réel, la réponse de tout le monde était «non».
Vlad

5

Je ne pense pas que ce sera le cas, car chaque code de bibliothèque qui appelle GetType déclarera la variable en tant que `` Object '' ou en tant que type générique `` T ''

Le code suivant:

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

imprime:

L'objet BadFoo dit qu'il est un 'TypeConcept.BadFoo'

L'objet NiceFoo dit qu'il est un 'TypeConcept.NiceFoo'

Le type générique BadFoo dit qu'il est un 'TypeConcept.BadFoo'

Le type générique NiceFoo dit qu'il est un 'TypeConcept.NiceFoo'

Le seul moment où ce type de code entraînera de mauvais scénarios est dans votre propre code, où vous déclarez le type de paramètre comme 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 dit qu'il est un 'System.Int32'

IFoo NiceFoo dit qu'il est un 'TypeConcept.NiceFoo'


4

Pour autant que je sache, le pire qui puisse arriver est d'induire en erreur des programmeurs innocents qui utilisent la classe empoisonnée, par exemple:

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

S'il myInstances'agit d'une instance d'une classe telle que celle que vous décrivez dans la question, elle sera simplement traitée comme un type inconnu.

Donc ma réponse est non, je ne vois aucune menace réelle ici.


1
Sûr. Un programmeur attentif peut voir au moment de la compilation quelle méthode "GetType" il invoque. Object.GetType()est distinct de SomeUserdefinedInterfaceClassOrStruct.GetType(). Seulement, si vous utilisez le dynamictype, vous ne pouvez jamais savoir ce qui se passera au moment de la liaison. Vous devriez donc utiliser dynamic x = expression; ... Type t = ((object)x).GetType();dans des cas comme celui-là.
Jeppe Stig Nielsen

@Jeppe points justes! Je pense que cela justifie une réponse séparée, ma réponse se concentre davantage sur un programmeur "innocent" qui ne sera pas si prudent.
Shadow Wizard est une oreille pour vous

3

Vous avez quelques options si vous voulez jouer en toute sécurité contre ce genre de piratage:

Lancer d'abord sur l'objet

Vous pouvez appeler la GetType()méthode d' origine en convertissant d'abord l'instance en object:

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

résulte en:

BadFoo says he's a 'ConsoleApplication.BadFoo'

Utiliser la méthode du modèle

L'utilisation de cette méthode de modèle vous donnera également le type réel:

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

GetType(badFoo);

2

Il y a une différence entre object.GetTypeet IFoo.GetType. GetTypeest appelé au moment de la compilation sur des objets inconnus, et non sur des interfaces. Dans votre exemple, avec la sortie, badFoo.GetTypeil est attendu un comportement, car vous surchargez la méthode. La seule chose est que d'autres programmeurs peuvent être confus à propos de ce comportement.

Mais si vous l'utilisez, typeof()il affichera que le type est le même et que vous ne pouvez pas écraser typeof().

Le programmeur peut également voir au moment de la compilation, quelle méthode GetTypeil invoque.

Donc, à votre question: ce modèle ne peut pas constituer une menace crédible, mais ce n'est pas non plus le meilleur style de codage.


badFoo.GetType()Est un comportement attendu, car il GetTypeétait surchargé.
huysentruitw
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.