C#
et Java
, l'un des principaux outils de la boîte à outils permettant de créer des composants réutilisables est le générique,
c'est-à-dire la possibilité de créer un composant qui peut fonctionner sur plusieurs types plutôt que sur un seul. Cela permet aux utilisateurs de
consommer ces composants et d'utiliser leurs propres types.echo
.any
:any
soit certainement générique en ce sens que la fonction acceptera n'importe quel type pour le type arg
,
nous perdons en fait les informations relatives à ce type lorsque le retour de la fonction a eu lieu.
Si nous avons passé un nombre, la seule information dont nous disposons est que tout type pourrait être retourné.T
à la fonction identity
.
Ce T
nous permet de capturer le type fourni par l'utilisateur (nombre, par exemple), afin que nous puissions utiliser ces informations ultérieurement.
Ici, nous utilisons à nouveau T
comme type de retour.
Lors de l'inspection, nous pouvons maintenant voir que le même type est utilisé pour
l'argument et le type de retour. Cela nous permet de véhiculer ces informations de type d'un côté et de l'autre de la fonction.identity
est générique, car elle fonctionne sur plusieurs types. Contrairement à l'utilisation de any
,
elle est tout aussi précise (c'est-à-dire qu'elle ne perd aucune information) comme la première fonction identity
qui utilisait des nombres
pour l'argument et le type renvoyés.identity
générique, nous pouvons l'appeler de deux manières. La première consiste à transmettre
tous les arguments, y compris l'argument type, à la fonction :T
comme chaîne de l'un des arguments de l'appel de la fonction, noté en utilisant <>
autour des arguments plutôt que ()
.T
pour nous en fonction du type de l'argument que nous transmettons :<>
);
le compilateur a juste regardé la valeur "myString
", et a défini T
sur son type.
Bien que l'inférence d'argument de type puisse être
un outil utile pour garder le code plus court et plus lisible, vous devrez peut-être explicitement passer les arguments de type
comme nous l'avons fait dans l'exemple précédent lorsque le compilateur ne parvient pas à inférer le type, comme cela peut arriver dans des exemples plus complexes.identity
,
le compilateur vous oblige à utiliser correctement tous les paramètres de type générique dans le corps de la fonction. C'est-à-dire que
vous traitez réellement ces paramètres comme s'ils pouvaient être de tous types.identity
de plus tôt :arg
sur la console à chaque appel ?
Nous pourrions être tentés d'écrire ceci :.length
membre de arg
, mais nous n’avons jamais dit que cet arg
avait ce membre.
Rappelez-vous, nous avons dit précédemment que ces variables de type remplacent tous les types, de sorte que quelqu'un utilisant cette fonction aurait pu
passer un nombre à la place, qui ne possède pas de membre .length
.T
plutôt que directement.
Puisque nous travaillons avec des tableaux, le membre .length
devrait être disponible.
Nous pouvons décrire cela de la même manière que nous créerions des tableaux d'autres types :loggingIdentity
comme suit :
"la fonction générique loggingIdentity
prend un paramètre de type T
et un argument arg
qui est un tableau de T
et renvoie un tableau de T".
Si nous passions dans un tableau de nombres, récupérer un tableau de nombres, car T
serait lié au nombre.
Cela nous permet d'utiliser notre variable de type générique T
dans les types avec lesquels nous travaillons,
plutôt que le type entier, ce qui nous donne une plus grande flexibilité.Array ‹T›
Dictionary‹string›
plutôt que simplement Dictionary
).
Cela rend le paramètre de type visible pour tous les autres membres de l'interface.GenericIdentityFn
, nous devrons également spécifier l'argument de type correspondant (ici: number
),
en verrouillant de manière efficace le contenu de la signature d'appel sous-jacente.
Comprendre quand placer le paramètre de type directement sur la signature de l'appel et le mettre sur l'interface elle-même sera
utile pour décrire les aspects d'un type qui sont génériques.<>
) à la suite du nom de la classe.GenericNumber
, mais vous avez peut-être remarqué que rien ne l'empêche
de n'utiliser que le type de number
. Nous aurions pu utiliser un string
à la place ou des objets encore plus complexes.loggingIdentity
, nous voulions pouvoir accéder à la propriété .length
de arg
,
mais le compilateur ne pouvait pas prouver que chaque type possédait une propriété .length
.
Il nous avertit donc que nous ne pouvons pas supposer cette hypothèse..length
.
Tant que le type a ce membre, nous le permettrons, mais il est obligatoire d'avoir au moins ce membre.
Pour ce faire, nous devons énumérer notre exigence en tant que contrainte sur ce que T
peut être.extends
pour indiquer notre contrainte :
Ici, nous allons créer une interface qui n'a qu'une propriété .length
.
obj
, nous allons donc placer une contrainte entre les deux types :