179 votes

Ce qui est la réification ?

Je sais que Java implémente polymorphisme paramétrique (Génériques) avec effacement. Je comprends ce que l'effacement est.

Je sais que C# implémente polymorphisme paramétrique avec de la réification. Je sais que pouvez-vous faire écrire

public void dosomething(List<String> input) {}
public void dosomething(List<Int> input) {}

ou que vous pouvez connaître lors de l'exécution à ce que le paramètre de type de certaines de paramétrer le type est, mais je ne comprends pas ce que c' est.

  • Qu'est ce qu'un réifiée type?
  • Qu'est ce qu'un réifiée de la valeur?
  • Ce qui se passe quand un type/valeur est réifié?

231voto

La réification est le processus de prendre une chose abstraite et la création d'un béton chose.

Le terme de réification en C# les génériques se réfère au processus par lequel un générique de la définition de type et d'un ou plusieurs type générique arguments (la chose abstraite) sont combinés pour créer un nouveau type générique (le concret de la chose).

À l'expression il différemment, c'est le processus de la définition d' List<T> et int et la production d'un béton List<int> type.

Pour comprendre, comparer les approches suivantes:

  • En Java, les génériques, un générique de la définition de type est transformé essentiellement un béton de type générique, partagés entre tous les permis argument de type combinaisons. Ainsi, plusieurs (code source) types sont mappés à une (niveau binaire) type - mais comme un résultat, des informations sur le type des arguments d'une instance est jeté dans cette instance (type d'effacement).

    1. Comme un effet secondaire de cette mise en œuvre technique, le seul type générique arguments qui sont nativement autorisées sont ces types qui peuvent partager le code binaire de leur type de béton; ce qui signifie que ces types dont les emplacements de stockage ont interchangeables représentations, ce qui signifie que les types de référence. Utilisation de types de valeur comme de type générique arguments nécessite de boxe entre eux (en les plaçant dans une simple référence de type wrapper).
    2. Pas de code est dupliqué afin de mettre en œuvre les génériques de cette façon.
    3. Type de l'information qui pourrait avoir été disponible au moment de l'exécution (à l'aide de réflexion), c'est perdu. Ceci, à son tour, signifie que la spécialisation d'un type générique (la capacité à utiliser un code source pour tout argument générique de la combinaison) est très restreinte.
    4. Ce mécanisme ne nécessite pas de support de l'environnement d'exécution.
    5. Il ya quelques solutions de contournement pour conserver les informations de type d'un programme Java ou JVM base de la langue peut utiliser.
  • En C#, les génériques, le générique de la définition de type est maintenu en mémoire lors de l'exécution. Chaque fois qu'un nouveau type de béton est nécessaire, l'environnement d'exécution combine le générique de la définition de type et le type des arguments et crée un nouveau type (réification). Nous obtenons donc un nouveau type pour chaque combinaison de type d'arguments, au moment de l'exécution.

    1. Cette mise en œuvre technique permet à tout type d'argument de type combinaison pour être instancié. Utilisation de types de valeur comme de type générique arguments ne cause pas de boxe, car ces types d'obtenir leur propre mise en œuvre. (Boxe existe encore dans le C#, bien sûr - mais il arrive dans d'autres scénarios, pas celui-ci.)
    2. La duplication de Code pourrait être un problème, mais dans la pratique il ne l'est pas, parce que suffisamment intelligent implémentations (cela inclut Microsoft .NET et Mono) peuvent partager le code pour certaines instances.
    3. Type d'information est maintenue, ce qui permet une spécialisation dans une certaine mesure, par l'examen des arguments de type à l'aide de la réflexion. Cependant, le degré de spécialisation est limitée, comme un résultat du fait que le générique de la définition de type est compilé avant tout de la réification qui se passe (ce qui est fait par la compilation de la définition contre les contraintes sur les paramètres de type - ainsi, le compilateur doit être capable de "comprendre" la définition même en l'absence d'arguments de type).
    4. Cette mise en œuvre technique dépend fortement de la prise en charge de runtime et JIT-compilation (c'est pourquoi on entend souvent dire que C# génériques ont quelques limitations sur les plateformes comme iOS, où la génération de code dynamique est restreint).
    5. Dans le contexte de C# génériques, la réification est fait pour vous par l'environnement d'exécution. Toutefois, si vous voulez plus de comprendre intuitivement la différence entre un générique de la définition de type et d'un béton de type générique, vous pouvez toujours effectuer une réification sur votre propre, en utilisant l' System.Type de la classe (même si l'argument de type générique de la combinaison de instancions n'apparaissent pas dans votre code source directement).
  • Dans les modèles C++, la définition du modèle est maintenu en mémoire au moment de la compilation. Chaque fois qu'une nouvelle instanciation d'un type de modèle est nécessaire dans le code source, le compilateur associe la définition du modèle et les arguments de modèle et crée un nouveau type (un processus qui correspond à notre définition initiale de la réification). Nous obtenons donc un nouveau type pour chaque combinaison des arguments de modèle, au moment de la compilation.

    1. Cette mise en œuvre technique permet à tout type d'argument de type combinaison pour être instancié.
    2. Ceci est connu pour dupliquer le code binaire, mais suffisamment intelligent outil de la chaîne peut encore détecter et de partager du code pour certaines instances.
    3. La définition du modèle lui-même n'est pas "compilé" - seulement des instanciations sont effectivement compilé. Cela impose moins de contraintes sur le compilateur et permet un plus grand degré de spécialisation de modèle.
    4. Depuis le modèle de instanciations sont effectuées au moment de la compilation, pas de support d'exécution est nécessaire ici.

27voto

Jon Hanna Points 40291

La réification signifie en général (en dehors de l'informatique) "de faire quelque chose de réel".

Dans la programmation, quelque chose est réifiée , si nous sommes capables d'accéder à des informations à ce sujet dans le langage lui-même.

Pour les deux entièrement non-génériques des exemples liés de quelque chose de C# et n'a pas réifiée, nous allons prendre les méthodes et les accès à la mémoire.

OO langues ont généralement des méthodes, (et beaucoup de ceux qui n'ont pas de fonctions qui sont semblables, mais n'est pas lié à une classe). En tant que tel, vous pouvez définir une méthode dans une telle langue, l'appeler, peut-être le remplacer, et ainsi de suite. Pas toutes ces langues vous permettent de réellement traiter avec la méthode elle-même en tant que données d'un programme. C# (et vraiment, .NET plutôt que C#) ne vous permettent de faire usage de l' MethodInfo des objets représentant les méthodes, donc en C# méthodes sont réifiée. Méthodes en C# sont "des objets de première classe".

Toutes les langues ont quelques moyens pour accéder à la mémoire d'un ordinateur. Dans un langage de bas niveau comme le C, nous pouvons traiter directement avec la correspondance entre les adresses numériques utilisés par l'ordinateur, de sorte que les goûts de int* ptr = (int*) 0xA000000; *ptr = 42; est raisonnable (tant que nous avons une bonne raison de soupçonner que l'accès à la mémoire de l'adresse 0xA000000 dans cette façon de ne pas faire sauter quelque chose). En C#, cela n'est pas raisonnable (nous pouvons juste au sujet de la force .NET, mais avec le .NET gestion de la mémoire bouger les choses autour d'elle n'est pas très susceptibles d'être utiles). C# n'est pas réifiée des adresses de mémoire.

Donc, comme refied signifie "fait de réels" un "réifiée de type" est un type qu'on peut "parler" dans la langue en question.

Dans les génériques, cela signifie deux choses.

L'une est qu' List<string> est un type comme string ou int . On peut comparer ce type, son nom, et en savoir davantage à ce sujet:

Console.WriteLine(typeof(List<string>).FullName); // System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Console.WriteLine(typeof(List<string>) == (42).GetType()); // False
Console.WriteLine(typeof(List<string>) == Enumerable.Range(0, 1).Select(i => i.ToString()).ToList().GetType()); // True
Console.WriteLine(typeof(List<string>).GenericTypeArguments[0] == typeof(string)); // True

Une conséquence de cela est que l'on peut "parler" d'une méthode générique (ou une méthode d'une classe générique) types des paramètres dans la méthode elle-même:

public static void DescribeType<T>(T element)
{
  Console.WriteLine(typeof(T).FullName);
}
public static void Main()
{
  DescribeType(42);               // System.Int32
  DescribeType(42L);              // System.Int64
  DescribeType(DateTime.UtcNow);  // System.DateTime
}

En règle générale, faire ce trop est "puant", mais il a beaucoup de cas utiles. Par exemple, regardez:

public static TSource Min<TSource>(this IEnumerable<TSource> source)
{
  if (source == null) throw Error.ArgumentNull("source");
  Comparer<TSource> comparer = Comparer<TSource>.Default;
  TSource value = default(TSource);
  if (value == null)
  {
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
      do
      {
        if (!e.MoveNext()) return value;
        value = e.Current;
      } while (value == null);
      while (e.MoveNext())
      {
        TSource x = e.Current;
        if (x != null && comparer.Compare(x, value) < 0) value = x;
      }
    }
  }
  else
  {
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
      if (!e.MoveNext()) throw Error.NoElements();
      value = e.Current;
      while (e.MoveNext())
      {
        TSource x = e.Current;
        if (comparer.Compare(x, value) < 0) value = x;
      }
    }
  }
  return value;
}

Ce n'est pas faire beaucoup de comparaisons entre le type d' TSource et les divers types de comportements différents (généralement un signe que vous ne devriez pas avoir utilisé des médicaments génériques à tous) mais il n'est partagé entre un chemin de code pour les types qui peuvent être null (doit renvoyer null si aucun élément trouvé, et ne doit pas faire des comparaisons pour trouver le minimum si l'un des éléments de comparaison est null) et le chemin de code pour les types qui ne peuvent pas être null (à lancer si aucun élément trouvé, et ne pas avoir à vous soucier de la possibilité de null des éléments).

Parce qu' TSource "le réel" dans la méthode, cette comparaison peut être faite soit au moment de l'exécution ou de jitting temps (généralement jitting temps, certainement le cas ci-dessus au jitting temps et de ne pas produire du code machine pour le chemin d'accès pas pris) et nous avoir une "vraie" version de la méthode pour chaque cas. (Même si, comme l'optimisation, le code de l'ordinateur est partagé par différentes méthodes pour les différentes de référence de type de type de paramètres, car il peut être, sans affecter le présent, et par conséquent, nous pouvons réduire la quantité de code machine jitted).

(Il n'est pas commun pour parler de la réification de types génériques en C#, sauf si vous traitent également de Java, car en C#, nous venons de prendre cette réification de tous les types sont réifiée. En Java, les non-types génériques sont appelés réifiée parce que c'est une distinction entre eux et les types génériques).

18voto

Luaan Points 8934

Comme duffymo déjà noté, "réification" n'est pas la clé de la différence.

En Java, les génériques sont essentiellement là pour améliorer le temps de compilation de soutien - il vous permet d'utiliser fortement typé par exemple des collections dans votre code, et le type de sécurité pris en charge pour vous. Cependant, ceci n'existe au moment de la compilation - le code compilé n'a plus aucune notion de génériques; tous les types génériques sont transformées en "béton" types (à l'aide d' object si le type générique est illimitée), en ajoutant les conversions de type et de type vérifications nécessaires.

Dans .NET, les génériques sont une partie intégrante de la CLR. Lorsque vous compilez un type générique, il reste génériques dans l'généré IL. Ce n'est pas seulement transformé en non-génériques de code en Java.

Ceci a de nombreuses incidences sur la manière dont les génériques de travail dans la pratique. Par exemple:

  • Java a SomeType<?> pour vous permettre de passer à toute mise en œuvre concrète d'un type générique. C# ne peut pas faire cela, chaque spécifique (réifiée) type générique est son propre type.
  • La surabondance de types génériques en Java à dire que leur valeur est stockée en tant qu' object. Cela peut avoir un impact sur les performances lors de l'utilisation de la valeur de types génériques. En C#, lorsque vous utilisez un type de valeur dans un type générique, il reste un type de valeur.

Pour donner un exemple, supposons que vous avez un List type générique avec un argument générique. En Java, List<String> et List<Int> , finissent par être exactement du même type à l'exécution - les types génériques seulement existent vraiment pour le moment de la compilation du code. Tous les appels, par exemple GetValue seront transformés en (String)GetValue et (Int)GetValue respectivement.

En C#, List<string> et List<int> sont de deux types différents. Ils ne sont pas interchangeables, et leur type de sécurité est appliquée dans l'exécution. Peu importe ce que vous faites, new List<int>().Add("SomeString") aura jamais de travail - le stockage sous-jacent en List<int> est vraiment quelque tableau d'entiers, alors qu'en Java, il est nécessairement object tableau. En C#, il n'y a pas de moulages, aucun de boxe etc.

Cela devrait également rendre évident pourquoi C# on ne peut pas faire la même chose que Java avec SomeType<?>. En Java, tous les types génériques ", dérivé de" SomeType<?> finissent par être exactement du même type. En C#, tous les divers spécifiques SomeType<T>s sont leur propre type. Suppression des contrôles de compilation, il est possible de transmettre SomeType<Int> au lieu de SomeType<String> (et vraiment, tout ce qu' SomeType<?> signifie "ignorer les contrôles de compilation pour le type générique"). En C#, il n'est pas possible, pas même pour les types dérivés (qui est, vous ne pouvez pas faire List<object> list = (List<object>)new List<string>(); même si string est dérivé de l' object).

Les deux implémentations ont leurs avantages et leurs inconvénients. Il y a eu quelques moments où j'aurais aimé être en mesure de simplement permettre l' SomeType<?> comme un argument en C#, mais il n'a tout simplement aucun sens de la façon dont C# génériques de travail.

3voto

duffymo Points 188155

La réification est un concept de modélisation orienté objet.

Reify est un verbe qui signifie "faire quelque chose d'abstrait réel" .

Lorsque vous faites de la programmation orientée objet, il est courant de modéliser des objets du monde réel en tant que composants logiciels (par exemple, fenêtre, bouton, personne, banque, véhicule, etc.).

Il est également courant de réifier les concepts abstraits en composants (par exemple, WindowListener, Broker, etc.)

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