72 votes

Comment, quand et où les méthodes génériques sont en béton ?

Cette question me demandais où le béton implementaiton d'une méthode générique en fait vient à l'existence. J'ai essayé google mais je ne suis pas à venir avec la bonne recherche.

Si nous prenons cet exemple simple:

class Program
{
    public static T GetDefault<T>()
    {
        return default(T);
    }

    static void Main(string[] args)
    {
        int i = GetDefault<int>();
        double d = GetDefault<double>();
        string s = GetDefault<string>();
    }
}

dans ma tête j'ai toujours supposé qu'à un certain point, il en résulte une mise en œuvre avec les 3 nécessaires des implémentations concrètes telles que, dans naïf pseudo mangling, nous aurions cette logique de béton implementaiton où la spécificité des types utilisés résultat dans la pile correct allocations etc.

class Program
{
    static void Main(string[] args)
    {
        int i = GetDefaultSystemInt32();
        double d = GetDefaultSystemFloat64();
        string s = GetDefaultSystemString();
    }

    static int GetDefaultSystemInt32()
    {
        int i = 0;
        return i;
    }
    static double GetDefaultSystemFloat64()
    {
        double d = 0.0;
        return d;
    }
    static string GetDefaultSystemString()
    {
        string s = null;
        return s;
    }
}

En regardant le IL pour le générique, il est toujours exprimée en termes de types génériques:

.method public hidebysig static !!T  GetDefault<T>() cil managed
{
  // Code size       15 (0xf)
  .maxstack  1
  .locals init ([0] !!T CS$1$0000,
           [1] !!T CS$0$0001)
  IL_0000:  nop
  IL_0001:  ldloca.s   CS$0$0001
  IL_0003:  initobj    !!T
  IL_0009:  ldloc.1
  IL_000a:  stloc.0
  IL_000b:  br.s       IL_000d
  IL_000d:  ldloc.0
  IL_000e:  ret
} // end of method Program::GetDefault

Alors, comment et à quel point il a été décidé qu'un int, puis un double, puis une chaîne doivent être alloués sur la pile et retournée à l'appelant? Est-ce une opération de l'équipe? Je suis à la recherche dans le complètement faux de la lumière?

78voto

Reed Copsey Points 315315

En C#, les concepts de types génériques et des méthodes est pris en charge par l'exécution elle-même. Le compilateur C# n'a pas besoin de créer une version concrète d'une méthode générique.

Le "béton" méthode générique est créé au moment de l'exécution par le JIT, et n'existe pas dans l'IL. La première fois, une méthode générique est utilisé avec un type, le JIT va voir si elle a été créée, et si non, de construire la méthode appropriée pour que le type générique.

C'est l'une des différences fondamentales entre les génériques et des choses comme les templates en C++. C'est aussi la raison principale pour beaucoup de limitations avec les génériques, car le compilateur n'est pas réellement de la création de l'exécution de la mise en œuvre pour les types, les restrictions de l'interface sont gérées par la compilation des contraintes de temps, qui font de génériques est un peu plus limité que dans les templates de C++ en termes de potentiel en cas d'utilisation. Cependant, le fait qu'ils sont pris en charge dans l'exécution elle-même permet la création de types génériques et l'utilisation de bibliothèques possible dans les moyens qui ne sont pas pris en charge en C++ et d'autres au moment de la compilation modèle créé implémentations.

45voto

Hans Passant Points 475940

La machine réelle, code pour une méthode générique est créé, comme toujours, lorsque la méthode est jitted. À ce stade, la gigue vérifie d'abord si un candidat a été jitted avant. Ce qui est très souvent le cas, le code d'une méthode dont le béton runtime type T est un type de référence doit être généré qu'une seule fois et est adapté pour chaque type de référence T. Les contraintes sur T assurez-vous que cette machine code est toujours valide, préalablement vérifiée par le compilateur C#.

Des exemplaires supplémentaires peuvent être générés pour les T sont des types valeur, leur code machine est différent parce que T les valeurs ne sont pas de simples pointeurs de plus.

Donc, oui, dans votre cas, vous vous retrouverez avec trois méthodes distinctes. L' <string> version serait utilisable pour n'importe quel type de référence, mais vous n'avez pas d'autres. Et l' <int> et <double> versions de l'adapter à la "T qui sont des types de valeur" à la catégorie.

Sinon, un excellent exemple, les valeurs de retour de ces méthodes sont transmis à l'appelant de manière différente. Sur la version x64 de la gigue, la version de chaîne renvoie la valeur avec le registre RAX, comme n'importe quel pointeur retourné valeur, int version revient avec le registre EAX, la double version de retour avec un XMM0 registre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X