Partage via


IntPtr numérique

Remarque

Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Elle inclut les changements de spécification proposés, ainsi que les informations nécessaires à la conception et au développement de la fonctionnalité. Ces articles sont publiés jusqu'à ce que les changements proposés soient finalisés et incorporés dans la spécification ECMA actuelle.

Il peut y avoir des différences entre la spécification de la fonctionnalité et l'implémentation réalisée. Ces différences sont consignées dans les notes pertinentes de la réunion de conception linguistique (LDM).

Pour en savoir plus sur le processus d'adoption des speclets de fonctionnalité dans la norme du langage C#, consultez l'article sur les spécifications.

Problème de champion : https://github.com/dotnet/csharplang/issues/6065

Récapitulatif

Il s’agit d’une révision sur la fonctionnalité initiale d’entiers natifs (spec), où les types nint/nuint étaient distincts des types sous-jacents System.IntPtr/System.UIntPtr. En résumé, nous traitons maintenant nint/nuint en tant qu’alias de types simples System.IntPtr/System.UIntPtr, comme nous le faisons pour int par rapport à System.Int32. L’indicateur de fonctionnalité runtime System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr déclenche ce nouveau comportement.

Création

8.3.5 Types simples

C# fournit un ensemble de types struct prédéfinis appelés types simples. Les types simples sont identifiés par le biais de mots clés, mais ces mots clés sont simplement des alias pour les types struct prédéfinis dans l’espace de noms System, comme décrit dans le tableau ci-dessous.

Mot-clé Type aliasé
sbyte System.SByte
byte System.Byte
short System.Int16
ushort System.UInt16
int System.Int32
uint System.UInt32
nint System.IntPtr
nuint System.UIntPtr
long System.Int64
ulong System.UInt64
char System.Char
float System.Single
double System.Double
bool System.Boolean
decimal System.Decimal

[...]

8.3.6 Types intégraux

C# prend en charge onze types entiers : sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, et char. [...]

8.8 Types non managés

En d’autres termes, un unmanaged_type est l’un des types suivants :

  • sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal ou bool.
  • Tout enum_type.
  • Tout struct_type défini par l’utilisateur qui n’est pas un type construit et ne contient que des champs de unmanaged_type.
  • Dans un code non sécurisé, tout pointer_type.

10.2.3 Conversions numériques implicites

Les conversions numériques implicites sont les suivantes :

  • De sbyte vers short, int, nint, long, float, double ou decimal.
  • De byte vers short, ushort, int, uint, nint, nuint, long, ulong, float, double ou decimal.
  • De short vers int, nint, long, float, double ou decimal.
  • De ushort vers int, uint, nint, nuint, long, ulong, float, double ou decimal.
  • De int vers nint, long, float, double ou decimal.
  • De uint vers nuint, long, ulong, float, double ou decimal.
  • De nint vers long, float, double ou decimal.
  • De nuint vers ulong, float, double ou decimal.
  • De long vers float, double ou decimal.
  • De ulong vers float, double ou decimal.
  • De char vers ushort, int, uint, nint, nuint, long, ulong, float, double ou decimal.
  • De float vers double.

[...]

10.2.11 Conversions d’expressions constantes implicites

Une conversion d’expression constante implicite permet les conversions suivantes :

  • Une constant_expression de type int peut être convertie en type sbyte, byte, short, ushort, uint, nint, nuint ou ulong, à condition que la valeur de constant_expression se trouve dans la plage du type de destination. [...]

10.3.2 Conversions numériques explicites

Les conversions numériques explicites sont les conversions d’un numeric_type vers un autre numeric_type pour lesquelles une conversion numérique implicite n’existe pas déjà :

  • De sbyte vers byte, ushort, uint, nuint, ulong ou char.
  • De byte vers sbyte ou char.
  • De short vers sbyte, byte, ushort, uint, nuint, ulong ou char.
  • De ushort vers sbyte, byte, short ou char.
  • De int vers sbyte, byte, short, ushort, uint, nuint, ulong ou char.
  • De uint vers sbyte, byte, short, ushort, int, nint ou char.
  • De long vers sbyte, byte, short, ushort, int, uint, nint, nuint, ulong ou char.
  • De nint vers sbyte, byte, short, ushort, int, uint, nuint, ulong ou char.
  • De nuint vers sbyte, byte, short, ushort, int, uint, nint, long ou char.
  • De ulong vers sbyte, byte, short, ushort, int, uint, nint, nuint, long ou char.
  • De char vers sbyte, byte ou short.
  • De float vers sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char ou decimal.
  • De double vers sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float ou decimal.
  • De decimal vers sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float ou double.

[...]

10.3.3 Conversions d’énumérations explicites

Les conversions d’énumérations explicites sont les suivantes :

  • De sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double ou decimal vers n’importe quel enum_type.
  • De n’importe quel enum_type vers sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double ou decimal.
  • De tout enum_type à tout autre enum_type.

12.6.4.7 Meilleure cible de conversion

Étant donné deux types T₁ et T₂, T₁ est une meilleure cible de conversion que T₂ si l'un des critères suivants est rempli :

  • Une conversion implicite de T₁ vers T₂ existe et aucune conversion implicite de T₂ vers T₁ n’existe
  • T₁ est Task<S₁>, T₂ est Task<S₂> et S₁ est une meilleure cible de conversion que S₂
  • T₁ est S₁ ou S₁?S₁ est un type intégral signé et T₂ est S₂ ou S₂?S₂ est un type intégral non signé. En particulier : [...]

12.8.12 Accès aux éléments

[...] Le nombre d’expressions dans la argument_list doit être identique au rang du array_type, et chaque expression doit être de type int, uint, nint, nuint, , long ou ulong,, ou doit être implicitement convertible dans l’un ou plusieurs de ces types.

11.8.12.2 Accès aux tableaux

[...] Le nombre d’expressions dans la argument_list doit être identique au rang du array_type, et chaque expression doit être de type int, uint, nint, nuint, , long ou ulong,, ou doit être implicitement convertible dans l’un ou plusieurs de ces types.

[...] Le traitement à l’exécution d’un accès à un tableau de la forme P[A], où P est une primary_no_array_creation_expression d’un array_type et A est une argument_list, consiste en les étapes suivantes : [...]

  • Les expressions d’index de la argument_list sont évaluées dans l’ordre, de gauche à droite. Après l'évaluation de chaque expression d'index, une conversion implicite vers l'un des types suivants est effectuée : int, uint, nint, nuint, long, ulong. Le premier type dans cette liste pour lequel une conversion implicite existe est choisi. [...]

12.8.16 Opérateurs suffixés d’incrémentation et de décrémentation

La résolution de surcharge des opérateurs unaires est appliquée pour sélectionner une implémentation spécifique de l’opérateur. Les opérateurs prédéfinis ++ et -- existent pour les types suivants : sbyte, byte, short, ushort, int, uint, nint, nuint,long, ulong, char, float, double, decimal et tout type enum.

12.9.2 Opérateur unaire plus

Les opérateurs unaire plus prédéfinis sont :

...
nint operator +(nint x);
nuint operator +(nuint x);

12.9.3 Opérateur unaire moins

Les opérateurs unaire moins prédéfinis sont :

  • Négation entière :

    ...
    nint operator –(nint x);
    

12.8.16 Opérateurs suffixés d’incrémentation et de décrémentation

Les opérateurs prédéfinis ++ et -- existent pour les types suivants : sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal et tout type enum.

11.7.19 Expressions de valeur par défaut

En outre, une default_value_expression est une expression constante si le type est l’un des types de valeur suivants : sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, ou tout type d’enum.

12.9.5 Opérateur complément binaire

Les opérateurs de complément bit à bit prédéfinis sont :

...
nint operator ~(nint x);
nuint operator ~(nuint x);

12.9.6 Opérateurs préfixés d’incrémentation et de décrémentation

Les opérateurs prédéfinis ++ et -- existent pour les types suivants : sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal et tout type enum.

12.10 opérateurs arithmétiques

12.10.2 opérateur de multiplication

Les opérateurs de multiplication prédéfinis sont listés ci-dessous. Tous les opérateurs calculent le produit de x et y.

  • Multiplication des entiers :

    ...
    nint operator *(nint x, nint y);
    nuint operator *(nuint x, nuint y);
    

12.10.3 opérateur de division

Les opérateurs de division prédéfinis sont listés ci-dessous. Tous les opérateurs calculent le quotient de x et y.

  • Division entière :

    ...
    nint operator /(nint x, nint y);
    nuint operator /(nuint x, nuint y);
    

12.10.4 Opérateur de reste

Les opérateurs de reste prédéfinis sont listés ci-dessous. Les opérateurs calculent tous le reste de la division entre x et y.

  • Reste entier :

    ...
    nint operator %(nint x, nint y);
    nuint operator %(nuint x, nuint y);
    

12.10.5 opérateur d’addition

  • Addition entière :

    ...
    nint operator +(nint x, nint y);
    nuint operator +(nuint x, nuint y);
    

12.10.6 Opérateur de soustraction

  • Soustraction entière :

    ...
    nint operator –(nint x, nint y);
    nuint operator –(nuint x, nuint y);
    

12.11 Opérateurs de décalage

Les opérateurs de décalage prédéfinis sont listés ci-dessous.

  • Décalage vers la gauche :

    ...
    nint operator <<(nint x, int count);
    nuint operator <<(nuint x, int count);
    
  • Décalage vers la droite :

    ...
    nint operator >>(nint x, int count);
    nuint operator >>(nuint x, int count);
    

    L’opérateur >> déplace x vers la droite d'un nombre de bits calculé comme décrit ci-dessous.

    Lorsque x est de type int, nint ou long, les bits de poids faible de x sont supprimés, les bits restants sont décalés vers la droite et les positions de bits vides de poids fort sont mises à zéro si x est non négatif, et mises à un si x est négatif.

    Lorsque x est de type uint, nuint ou ulong, les bits de poids faible de x sont supprimés, les bits restants sont décalés vers la droite et les positions de bits vides de poids fort sont mises à zéro.

  • Décalage logique à droite :

    ...
    nint operator >>>(nint x, int count);
    nuint operator >>>(nuint x, int count);
    

Pour les opérateurs prédéfinis, le nombre de bits à décaler est calculé comme suit : [...]

  • Lorsque le type de x est nint ou nuint, le décalage est déterminé par les cinq bits de poids faible de count sur une plateforme 32 bits, ou les six bits de poids faible de count sur une plateforme 64 bits.

12.12 opérateurs relationnels et de test de type

12.12.2 opérateurs de comparaison entiers

Les opérateurs de comparaison entiers prédéfinis sont :

...
bool operator ==(nint x, nint y);
bool operator ==(nuint x, nuint y);

bool operator !=(nint x, nint y);
bool operator !=(nuint x, nuint y);

bool operator <(nint x, nint y);
bool operator <(nuint x, nuint y);

bool operator >(nint x, nint y);
bool operator >(nuint x, nuint y);

bool operator <=(nint x, nint y);
bool operator <=(nuint x, nuint y);

bool operator >=(nint x, nint y);
bool operator >=(nuint x, nuint y);

12.12 Opérateurs logiques

12.12.2 Opérateurs logiques entiers

Les opérateurs logiques entiers prédéfinis sont :

...
nint operator &(nint x, nint y);
nuint operator &(nuint x, nuint y);

nint operator |(nint x, nint y);
nuint operator |(nuint x, nuint y);

nint operator ^(nint x, nint y);
nuint operator ^(nuint x, nuint y);

12.22 Expressions constantes

Une expression constante peut être un type de valeur ou un type de référence. Si une expression constante est un type de valeur, il doit s’agir de l’un des types suivants : sbyte, byte, short, ushort, int, uint, nint, nuint, long, ulong, char, float, double, decimal, bool, ou tout type d’enum.

[...]

Une conversion implicite d’expression constante permet de convertir une expression constante de type int en sbyte, byte, short, ushort, uint, nint, nuint, ou ulong, à condition que la valeur de l’expression constante se trouve dans la plage du type de destination.

17.4 Accès aux éléments de tableau

Les éléments de tableau sont accessibles à l’aide d’expressions element_access de la forme A[I₁, I₂, ..., Iₓ], où A est une expression de type de tableau et chaque Iₑ est une expression de type int, uint, nint, nuint,long, ulong ou peut être implicitement convertie en un ou plusieurs de ces types. Le résultat de l’accès à un élément de tableau est une variable, à savoir l’élément de tableau sélectionné par les index.

23.5 Conversions de pointeur

23.5.1 Général

[...]

En outre, dans un contexte non sécurisé, l’ensemble de conversions explicites disponibles est étendu pour inclure les conversions de pointeurs explicites suivantes :

  • De tout pointer_type à tout autre pointer_type.
  • De sbyte, byte, short, ushort, int, uint, nint, nuint,long ou ulong vers n’importe quel pointer_type.
  • De n’importe quel pointer_type vers sbyte, byte, short, ushort, int, uint, nint, nuint,long ou ulong.

23.6.4 Accès aux éléments de pointeur

[...] Dans un accès à un élément de pointeur sous la forme P[E], P doit être une expression de type de pointeur autre que void*, et E doit être une expression pouvant être implicitement convertie en int, uint, nint, nuint, long ou ulong.

23.6.7 Arithmétique des pointeurs

Dans un contexte non sécurisé, l’opérateur + et l’opérateur peuvent être appliqués aux valeurs de tous les types de pointeurs, à l‘exception de void*. Ainsi, pour chaque type de pointeur T*, les opérateurs suivants sont implicitement définis :

[...]
T* operator +(T* x, nint y);
T* operator +(T* x, nuint y);
T* operator +(nint x, T* y);
T* operator +(nuint x, T* y);
T* operator -(T* x, nint y);
T* operator -(T* x, nuint y);

Étant donné une expression P de type de pointeur T* et une expression N de type int, uint, nint, nuint, long ou ulong, les expressions P + N et N + P calculent la valeur du pointeur de type T* qui résulte de l’ajout de N * sizeof(T) à l’adresse donnée par P. De même, l’expression P – N calcule la valeur du pointeur de type T* qui résulte de la soustraction de N * sizeof(T) de l’adresse donnée par P.

Considérations diverses

Changements cassants

L'un des principaux impacts de cette conception est que System.IntPtr et System.UIntPtr gagnent des opérateurs intégrés (conversions, unaires et binaires).
Ceux-ci incluent les opérateurs checked, ce qui signifie que les opérateurs suivants sur ces types lanceront désormais une exception en cas de dépassement :

  • IntPtr + int
  • IntPtr - int
  • IntPtr -> int
  • long -> IntPtr
  • void* -> IntPtr

Encodage des métadonnées

Cette conception signifie que nint et nuint peuvent simplement être émis en tant que System.IntPtr et System.UIntPtr, sans utiliser System.Runtime.CompilerServices.NativeIntegerAttribute.
De même, lors du chargement des métadonnées, NativeIntegerAttribute peut être ignoré.