Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka
Tento článek je specifikace funkce. Specifikace slouží jako návrhový dokument pro funkci. Zahrnuje navrhované změny specifikace spolu s informacemi potřebnými při návrhu a vývoji funkce. Tyto články se publikují, dokud nebudou navrhované změny specifikace finalizovány a začleněny do aktuální specifikace ECMA.
Mezi specifikací funkce a dokončenou implementací může docházet k nějakým nesrovnalostem. Tyto rozdíly jsou zachyceny v relevantních poznámkách schůzky návrhu jazyka (LDM) .
Další informace o procesu přijetí specifikací funkcí do jazyka C# najdete v článku o specifikacích .
Problém šampiona: https://github.com/dotnet/csharplang/issues/435
Shrnutí
Jazyková podpora pro typy celých čísel s nativní velikostí, se znaménkem i bez něj.
Motivací jsou scénáře interoperability a knihovny na nízké úrovni.
Design
Identifikátory nint a nuint jsou nová kontextová klíčová slova, která představují nativní typy celých čísel se znaménkem a bez znaménka.
Identifikátory jsou považovány pouze za klíčová slova, pokud vyhledávání názvů nenajde v daném umístění programu realizovatelný výsledek.
nint x = 3;
_ = nint.Equals(x, 3);
Typy nint a nuint jsou reprezentovány podkladovými typy System.IntPtr a System.UIntPtr s tím, že kompilátor poskytuje další převody a operace pro tyto typy jako nativní celočíselné typy.
Konstanty
Konstantní výrazy mohou být typu nint nebo nuint.
Pro nativní int literály neexistuje žádná přímá syntaxe. Místo toho lze použít implicitní nebo explicitní přetypování jiných celočíselných konstantních hodnot: const nint i = (nint)42;.
nint konstanty jsou v rozsahu [ int.MinValue, int.MaxValue ].
nuint konstanty jsou v rozsahu [ uint.MinValue, uint.MaxValue ].
V MinValue nebo MaxValue neexistují žádná pole nint ani nuint, protože kromě nuint.MinValuenelze tyto hodnoty vygenerovat jako konstanty.
Konstantní skládání je podporováno pro všechny unární operátory { +, -, ~ } a binární operátory { +, -, *, /, %, ==, !=, <, <=, >, >=, &, |, ^, <<, >> }.
Konstantní operace skládání se vyhodnocují pomocí Int32 a UInt32 operandů, nikoli nativních int pro konzistentní chování bez ohledu na platformu kompilátoru.
Pokud má operace za následek konstantní hodnotu v 32bitových bitech, provádí se při kompilaci konstantní skládání.
Jinak se operace provádí za běhu a nepovažuje se za konstantu.
Konverzace
Mezi nint a IntPtra mezi nuint a UIntPtrexistuje převod identity .
Existuje převod identity mezi složenými typy, které se liší pouze nativními inty a podkladovými typy: pole, Nullable<>, vytvořené typy a řazené kolekce členů.
Následující tabulky popisují převody mezi speciálními typy.
(IL pro každý převod zahrnuje varianty pro kontexty unchecked a checked, pokud se liší.)
Obecné poznámky k následující tabulce:
-
conv.uje převod na nativní celé číslo s nulovým rozšířením aconv.ije převod na nativní celé číslo s rozšířením znaménka. -
checkedkontexty pro rozšiřující i zužující jsou:-
conv.ovf.*prosigned to * -
conv.ovf.*.unprounsigned to *
-
-
uncheckedkontexty pro rozšiřující jsou:-
conv.i*prosigned to *(kde * je cílová šířka) -
conv.u*prounsigned to *(kde * je cílová šířka)
-
- Kontexty pro zúžení
uncheckedjsou:-
conv.i*proany to signed *(kde * je cílová šířka) -
conv.u*proany to unsigned *(kde * je cílová šířka)
-
Několik příkladů:
-
sbyte to nintasbyte to nuintpoužívajíconv.i, zatímcobyte to nintabyte to nuintpoužívajíconv.u, protože jsou všechny rozšiřující. -
nint to byteanuint to bytepoužívajíconv.u1, zatímconint to sbyteanuint to sbytepoužívajíconv.i1. Probyte,sbyte,shortaushortje typ zásobníkuint32. Takžeconv.i1je efektivně "převeden na podepsaný byte a poté rozšířen na int32", zatímcoconv.u1je efektivně "převeden na nepodepsaný byte a poté rozšířen s nulovým rozšířením na int32". -
checked void* to nintpoužíváconv.ovf.i.unstejným způsobem jakochecked void* to longpoužíváconv.ovf.i8.un.
| Operand | Cíl | Přeměna | IL |
|---|---|---|---|
object |
nint |
Rozbalení | unbox |
void* |
nint |
PointerToVoid | Nop/ conv.ovf.i.un |
sbyte |
nint |
Implicitní číslo | conv.i |
byte |
nint |
Implicitní číslo | conv.u |
short |
nint |
Implicitní číslo | conv.i |
ushort |
nint |
Implicitní číslo | conv.u |
int |
nint |
Implicitní číslo | conv.i |
uint |
nint |
Explicitně číselný | conv.u / conv.ovf.i.un |
long |
nint |
Explicitně číselný | conv.i / conv.ovf.i |
ulong |
nint |
Explicitně číselný | conv.i / conv.ovf.i.un |
char |
nint |
Implicitní číslo | conv.u |
float |
nint |
Explicitně číselný | conv.i / conv.ovf.i |
double |
nint |
Explicitně číselný | conv.i / conv.ovf.i |
decimal |
nint |
Explicitně číselný | long decimal.op_Explicit(decimal) conv.i / ... conv.ovf.i |
IntPtr |
nint |
Identita | |
UIntPtr |
nint |
Žádný | |
object |
nuint |
Rozbalení | unbox |
void* |
nuint |
PointerToVoid | Nop |
sbyte |
nuint |
Explicitně číselný | conv.i / conv.ovf.u |
byte |
nuint |
Implicitní číslo | conv.u |
short |
nuint |
Explicitně číselný | conv.i / conv.ovf.u |
ushort |
nuint |
Implicitní číslo | conv.u |
int |
nuint |
Explicitně číselný | conv.i / conv.ovf.u |
uint |
nuint |
Implicitní číslo | conv.u |
long |
nuint |
Explicitně číselný | conv.u / conv.ovf.u |
ulong |
nuint |
Explicitně číselný | conv.u / conv.ovf.u.un |
char |
nuint |
Implicitní číslo | conv.u |
float |
nuint |
Explicitně číselný | conv.u / conv.ovf.u |
double |
nuint |
Explicitně číselný | conv.u / conv.ovf.u |
decimal |
nuint |
Explicitně číselný | ulong decimal.op_Explicit(decimal) conv.u / ... conv.ovf.u.un |
IntPtr |
nuint |
Žádný | |
UIntPtr |
nuint |
Identita | |
| Výčet | nint |
ExplicitEnumeration | |
| Výčet | nuint |
ExplicitEnumeration |
| Operand | Cíl | Přeměna | IL |
|---|---|---|---|
nint |
object |
Box | box |
nint |
void* |
PointerToVoid | Nop/ conv.ovf.u |
nint |
nuint |
Explicitně číselný |
conv.u (lze vynechat) / conv.ovf.u |
nint |
sbyte |
Explicitně číselný | conv.i1 / conv.ovf.i1 |
nint |
byte |
Explicitně číselný | conv.u1 / conv.ovf.u1 |
nint |
short |
Explicitně číselný | conv.i2 / conv.ovf.i2 |
nint |
ushort |
Explicitně číselný | conv.u2 / conv.ovf.u2 |
nint |
int |
Explicitně číselný | conv.i4 / conv.ovf.i4 |
nint |
uint |
Explicitně číselný | conv.u4 / conv.ovf.u4 |
nint |
long |
Implicitní číslo | conv.i8 |
nint |
ulong |
Explicitně číselný | conv.i8 / conv.ovf.u8 |
nint |
char |
Explicitně číselný | conv.u2 / conv.ovf.u2 |
nint |
float |
Implicitní číslo | conv.r4 |
nint |
double |
Implicitní číslo | conv.r8 |
nint |
decimal |
Implicitní číslo | conv.i8 decimal decimal.op_Implicit(long) |
nint |
IntPtr |
Identita | |
nint |
UIntPtr |
Žádný | |
nint |
Výčet | ExplicitEnumeration | |
nuint |
object |
Box | box |
nuint |
void* |
PointerToVoid | Nop |
nuint |
nint |
Explicitně číselný |
conv.i(lze vynechat) / conv.ovf.i.un |
nuint |
sbyte |
Explicitně číselný | conv.i1 / conv.ovf.i1.un |
nuint |
byte |
Explicitně číselný | conv.u1 / conv.ovf.u1.un |
nuint |
short |
Explicitně číselný | conv.i2 / conv.ovf.i2.un |
nuint |
ushort |
Explicitně číselný | conv.u2 / conv.ovf.u2.un |
nuint |
int |
Explicitně číselný | conv.i4 / conv.ovf.i4.un |
nuint |
uint |
Explicitně číselný | conv.u4 / conv.ovf.u4.un |
nuint |
long |
Explicitně číselný | conv.u8 / conv.ovf.i8.un |
nuint |
ulong |
Implicitní číslo | conv.u8 |
nuint |
char |
Explicitně číselný | conv.u2 / conv.ovf.u2.un |
nuint |
float |
Implicitní číslo | conv.r.un conv.r4 |
nuint |
double |
Implicitní číslo | conv.r.un conv.r8 |
nuint |
decimal |
Implicitní číslo | conv.u8 decimal decimal.op_Implicit(ulong) |
nuint |
IntPtr |
Žádný | |
nuint |
UIntPtr |
Identita | |
nuint |
Výčet | ExplicitEnumeration |
Převod z A na Nullable<B> je:
- implicitní nulovatelný převod, pokud existuje převod identity nebo implicitní převod z
AnaB; - explicitní nulovatelný převod, pokud existuje explicitní převod z
AnaB; - v opačném případě je neplatný.
Převod z Nullable<A> na B je:
- explicitní převod s možnou hodnotou null, pokud existuje převod identity nebo implicitní nebo explicitní číselný převod z
AnaB; - v opačném případě je neplatný.
Převod z Nullable<A> na Nullable<B> je:
- převod identity, pokud existuje převod identity z
AnaB; - explicitní nulový převod, pokud existuje implicitní nebo explicitní číselný převod z
AnaB; - v opačném případě je neplatný.
Operátoři
Předdefinované operátory jsou následující.
Tyto operátory jsou považovány za přetížené na základě normálních pravidel implicitních převodů , pokud alespoň jeden z operandů je typu nint nebo nuint.
(Il pro každého operátora obsahuje varianty pro kontexty unchecked a checked, pokud se liší.)
| Unární | Podpis operátoru | IL |
|---|---|---|
+ |
nint operator +(nint value) |
nop |
+ |
nuint operator +(nuint value) |
nop |
- |
nint operator -(nint value) |
neg |
~ |
nint operator ~(nint value) |
not |
~ |
nuint operator ~(nuint value) |
not |
| Binární | Podpis operátoru | IL |
|---|---|---|
+ |
nint operator +(nint left, nint right) |
add / add.ovf |
+ |
nuint operator +(nuint left, nuint right) |
add / add.ovf.un |
- |
nint operator -(nint left, nint right) |
sub / sub.ovf |
- |
nuint operator -(nuint left, nuint right) |
sub / sub.ovf.un |
* |
nint operator *(nint left, nint right) |
mul / mul.ovf |
* |
nuint operator *(nuint left, nuint right) |
mul / mul.ovf.un |
/ |
nint operator /(nint left, nint right) |
div |
/ |
nuint operator /(nuint left, nuint right) |
div.un |
% |
nint operator %(nint left, nint right) |
rem |
% |
nuint operator %(nuint left, nuint right) |
rem.un |
== |
bool operator ==(nint left, nint right) |
beq / ceq |
== |
bool operator ==(nuint left, nuint right) |
beq / ceq |
!= |
bool operator !=(nint left, nint right) |
bne |
!= |
bool operator !=(nuint left, nuint right) |
bne |
< |
bool operator <(nint left, nint right) |
blt / clt |
< |
bool operator <(nuint left, nuint right) |
blt.un / clt.un |
<= |
bool operator <=(nint left, nint right) |
ble |
<= |
bool operator <=(nuint left, nuint right) |
ble.un |
> |
bool operator >(nint left, nint right) |
bgt / cgt |
> |
bool operator >(nuint left, nuint right) |
bgt.un / cgt.un |
>= |
bool operator >=(nint left, nint right) |
bge |
>= |
bool operator >=(nuint left, nuint right) |
bge.un |
& |
nint operator &(nint left, nint right) |
and |
& |
nuint operator &(nuint left, nuint right) |
and |
| |
nint operator |(nint left, nint right) |
or |
| |
nuint operator |(nuint left, nuint right) |
or |
^ |
nint operator ^(nint left, nint right) |
xor |
^ |
nuint operator ^(nuint left, nuint right) |
xor |
<< |
nint operator <<(nint left, int right) |
shl |
<< |
nuint operator <<(nuint left, int right) |
shl |
>> |
nint operator >>(nint left, int right) |
shr |
>> |
nuint operator >>(nuint left, int right) |
shr.un |
U některých binárních operátorů podporují operátory IL další typy operandů (viz tabulka typů ECMA-335 III.1.5 Operand). Sada typů operandů podporovaných jazykem C# je ale omezená pro jednoduchost a konzistenci s existujícími operátory v jazyce.
Jsou podporovány zdvižené verze operátorů, kde jsou argumenty a návratové typy nint? a nuint?.
Operace složeného přiřazení x op= y, kde x nebo y jsou nativní inty, se řídí stejnými pravidly jako u jiných primitivních typů s předem definovanými operátory.
Konkrétně je výraz vázán jako x = (T)(x op y), kde T je typ x a kde x je vyhodnocen pouze jednou.
Operátory posunu by měly maskovat počet bitů, které se mají posunout – na 5 bitů, pokud sizeof(nint) je 4 a 6 bitů, pokud sizeof(nint) je 8.
(Viz §12.11) ve specifikaci jazyka C#).
Kompilátor C#9 bude hlásit chyby vazby na předdefinované nativní celočíselné operátory při kompilaci s dřívější jazykovou verzí, ale umožní používat předdefinované převody do a z nativních celých čísel.
csc -langversion:9 -t:library A.cs
public class A
{
public static nint F;
}
csc -langversion:8 -r:A.dll B.cs
class B : A
{
static void Main()
{
F = F + 1; // error: nint operator+ not available with -langversion:8
F = (System.IntPtr)F + 1; // ok
}
}
Ukazatelová aritmetika
V jazyce C# nejsou žádné předdefinované operátory pro sčítání nebo odčítání nativního celočíselného posunu u ukazatele.
Místo toho jsou hodnoty nint a nuint povýšeny na long a ulong a aritmetické operátory se pro tyto typy používají předdefinovaným způsobem.
static T* AddLeftS(nint x, T* y) => x + y; // T* operator +(long left, T* right)
static T* AddLeftU(nuint x, T* y) => x + y; // T* operator +(ulong left, T* right)
static T* AddRightS(T* x, nint y) => x + y; // T* operator +(T* left, long right)
static T* AddRightU(T* x, nuint y) => x + y; // T* operator +(T* left, ulong right)
static T* SubRightS(T* x, nint y) => x - y; // T* operator -(T* left, long right)
static T* SubRightU(T* x, nuint y) => x - y; // T* operator -(T* left, ulong right)
Binární číselné povýšení
Informativní text binárních číselných propagací (viz §12.4.7.3ve specifikaci jazyka C#) se aktualizuje následujícím způsobem:
- …
- V opačném případě, je-li jeden operand typu
ulong, druhý operand je převeden na typulong, nebo dojde k chybě doby vazby, pokud druhý operand je typusbyte,short,int,nintnebolong.- Jinak, pokud je jeden operand typu
nuint, druhý operand je převeden na typnuint, nebo dojde k chybě vazby v případě, že druhý operand je typusbyte,short,int,nintnebolong.- V opačném případě je-li jeden operand typu
long, druhý operand je převeden na typlong.- V opačném případě, je-li jeden operand typu
uinta druhý operand je typusbyte,short,nint, neboint, oba operandy jsou převedeny na typlong.- V opačném případě je-li jeden operand typu
uint, druhý operand je převeden na typuint.- Jinak, pokud je některý operand typu
nint, druhý operand je převeden na typnint.- V opačném případě jsou oba operandy převedeny na typ
int.
Dynamický
Převody a operátory jsou syntetizovány kompilátorem a nejsou součástí podkladových IntPtr a UIntPtr typů.
V důsledku toho nejsou tyto převody a operátory k dispozici z pořadače modulu runtime pro dynamic.
nint x = 2;
nint y = x + x; // ok
dynamic d = x;
nint z = d + x; // RuntimeBinderException: '+' cannot be applied 'System.IntPtr' and 'System.IntPtr'
Členy typu
Jediný konstruktor pro nint nebo nuint je konstruktor bez parametrů.
Z
// constructors
// arithmetic operators
// implicit and explicit conversions
public static readonly IntPtr Zero; // use 0 instead
public static int Size { get; } // use sizeof() instead
public static IntPtr Add(IntPtr pointer, int offset);
public static IntPtr Subtract(IntPtr pointer, int offset);
public int ToInt32();
public long ToInt64();
public void* ToPointer();
Zbývající členové System.IntPtr a System.UIntPtrjsou implicitně zahrnuti do nint a nuint. Pro rozhraní .NET Framework 4.7.2:
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);
Rozhraní implementovaná System.IntPtr a System.UIntPtrjsou implicitně zahrnuta v nint a nuint, přičemž výskyty základních typů jsou nahrazeny odpovídajícími nativními celočíselnými typy.
Pokud například IntPtr implementuje ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>, nint implementuje ISerializable, IEquatable<nint>, IComparable<nint>.
Přepsání, skrytí a implementace
nint a System.IntPtra nuint a System.UIntPtrjsou považovány za ekvivalenty pro přepsání, skrytí a implementaci.
Přetížení se nemohou lišit pouze podle nint a System.IntPtr, ani pouze podle nuint a System.UIntPtr.
Přepsání a implementace se mohou lišit pouze podle nint a System.IntPtr, nebo nuint a System.UIntPtr.
Metody skrývají jiné metody, které se liší pouze v parametrech nint a System.IntPtr, nebo nuint a System.UIntPtr.
Různé
Výrazy nint a nuint, použité jako indexy pole, se emitují bez převodu.
static object GetItem(object[] array, nint index)
{
return array[index]; // ok
}
nint a nuint nelze použít jako základní typ enum z jazyka C#.
enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}
Čtení a zápisy jsou atomické pro nint a nuint.
Pole mohou být označena volatile pro typy nint a nuint.
ECMA-334 15.5.4 nezahrnuje enum se základním typem System.IntPtr ani System.UIntPtr.
default(nint) a new nint() jsou ekvivalentní (nint)0; default(nuint) a new nuint() jsou ekvivalentní (nuint)0.
typeof(nint) je typeof(IntPtr); typeof(nuint) je typeof(UIntPtr).
sizeof(nint) a sizeof(nuint) jsou podporované, ale vyžadují kompilaci v nebezpečném kontextu (podle potřeby pro sizeof(IntPtr) a sizeof(UIntPtr)).
Hodnoty nejsou konstanty kompilace.
sizeof(nint) se implementuje jako sizeof(IntPtr) místo IntPtr.Size; sizeof(nuint) se implementuje jako sizeof(UIntPtr) místo UIntPtr.Size.
Diagnostika kompilátoru pro odkazy na typy zahrnující nint nebo nuint hlásí nint nebo nuint místo IntPtr nebo UIntPtr.
Metaúdaje
nint a nuint jsou reprezentovány v metadatech jako System.IntPtr a System.UIntPtr.
Odkazy na typy, které obsahují nint nebo nuint, se emitují s System.Runtime.CompilerServices.NativeIntegerAttribute, aby indikovali, které části odkazu na typ jsou nativní čísla integer.
namespace System.Runtime.CompilerServices
{
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Event |
AttributeTargets.Field |
AttributeTargets.GenericParameter |
AttributeTargets.Parameter |
AttributeTargets.Property |
AttributeTargets.ReturnValue,
AllowMultiple = false,
Inherited = false)]
public sealed class NativeIntegerAttribute : Attribute
{
public NativeIntegerAttribute()
{
TransformFlags = new[] { true };
}
public NativeIntegerAttribute(bool[] flags)
{
TransformFlags = flags;
}
public readonly bool[] TransformFlags;
}
}
Kódování odkazů na typ s NativeIntegerAttribute je popsáno v NativeIntegerAttribute.md.
Alternativy
Alternativou k výše uvedenému přístupu k vymazání typu je zavedení nových typů: System.NativeInt a System.NativeUInt.
public readonly struct NativeInt
{
public IntPtr Value;
}
Různé typy by umožňovaly přetížení odlišné od IntPtr a umožnily by specifické parsování a ToString().
CLR by mělo více práce, aby tyto typy zvládalo efektivně, což zmaří primární účel této funkce - efektivitu.
Spolupráce s existujícím nativním int kódem, který používá IntPtr, by byla obtížnější.
Další alternativou je přidání nativní podpory int pro IntPtr v rámci, ale bez podpory konkrétního kompilátoru.
Kompilátor automaticky podporuje všechny nové převody a aritmetické operace.
Jazyk ale neposkytuje klíčová slova, konstanty ani checked operace.
Schůzky o designu
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-05-26.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-06-13.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-07-05.md#native-int-and-intptr-operators
- https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-10-23.md
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-03-25.md
C# feature specifications