La réification signifie généralement (en dehors de l'informatique) "faire quelque chose de réel".
En programmation, quelque chose est réifié si nous pouvons accéder à des informations à son sujet dans le langage lui-même.
Pour deux exemples complètement non génériques de quelque chose que C # fait et n'a pas réifié, prenons les méthodes et l'accès à la mémoire.
Les langages OO ont généralement des méthodes (et beaucoup d'entre eux n'ont pas de fonctions similaires mais non liées à une classe). En tant que tel, vous pouvez définir une méthode dans un tel langage, l'appeler, peut-être la remplacer, etc. Tous ces langages ne vous permettent pas de traiter la méthode elle-même en tant que données pour un programme. C # (et vraiment, .NET plutôt que C #) vous permet d'utiliser des MethodInfo
objets représentant les méthodes, donc en C #, les méthodes sont réifiées. Les méthodes en C # sont des "objets de première classe".
Tous les langages pratiques ont des moyens d'accéder à la mémoire d'un ordinateur. Dans un langage de bas niveau comme C, nous pouvons traiter directement le mappage entre les adresses numériques utilisées par l'ordinateur, donc les goûts int* ptr = (int*) 0xA000000; *ptr = 42;
sont raisonnables (tant que nous avons une bonne raison de soupçonner que l'accès à l'adresse mémoire 0xA000000
de cette manière a gagné '' t faire sauter quelque chose). En C #, ce n'est pas raisonnable (nous pouvons à peu près le forcer dans .NET, mais avec la gestion de la mémoire .NET, ce n'est pas très susceptible d'être utile). C # n'a pas d'adresses mémoire réifiées.
Ainsi, comme refied signifie «rendu réel», un «type réifié» est un type dont nous pouvons «parler» dans la langue en question.
En générique, cela signifie deux choses.
La première est que List<string>
c'est un type tel string
ou tel int
. Nous pouvons comparer ce type, obtenir son nom et s'enquérir:
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 ceci est que nous pouvons "parler" des types de paramètres d'une méthode générique (ou d'une méthode d'une classe générique) 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, en faire trop est «malodorant», mais cela présente de nombreux 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;
}
Cela ne fait pas beaucoup de comparaisons entre le type de TSource
et divers types pour différents comportements (généralement un signe que vous n'auriez pas dû utiliser du tout des génériques) mais il se partage entre un chemin de code pour les types qui peuvent être null
(devrait retourner null
si aucun élément trouvé, et ne doit pas faire de comparaisons pour trouver le minimum si l'un des éléments comparés est null
) et le chemin du code pour les types qui ne peuvent pas l'être null
(devrait lancer si aucun élément trouvé, et n'a pas à s'inquiéter de la possibilité d' null
éléments ).
Parce qu'il TSource
est "réel" dans la méthode, cette comparaison peut être faite soit au moment de l'exécution, soit au moment du jitting (généralement au moment du jitting, certainement le cas ci-dessus le ferait au moment du jitting et ne produirait pas de code machine pour le chemin non pris) et nous avons un version «réelle» distincte de la méthode pour chaque cas. (Bien qu'à titre d'optimisation, le code machine est partagé pour différentes méthodes pour différents paramètres de type de référence, car cela peut être sans affecter cela, et nous pouvons donc réduire la quantité de code machine jit).
(Il n'est pas courant de parler de réification de types génériques en C #, sauf si vous traitez également avec Java, car en C # nous tenons simplement cette réification pour acquise; tous les types sont réifiés. En Java, les types non génériques sont appelés réifiés car cela est une distinction entre eux et les types génériques).