Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Jegyzet
Ez a cikk egy funkcióspecifikáció. A specifikáció a funkció tervezési dokumentumaként szolgál. Tartalmazza a specifikáció javasolt módosításait, valamint a funkció tervezése és fejlesztése során szükséges információkat. Ezeket a cikkeket mindaddig közzéteszik, amíg a javasolt specifikációmódosításokat nem véglegesítik, és be nem építik a jelenlegi ECMA-specifikációba.
A szolgáltatás specifikációja és a befejezett implementáció között eltérések lehetnek. Ezeket a különbségeket a vonatkozó nyelvi tervezési értekezlet (LDM) megjegyzései rögzítik.
A funkcióspektusok C# nyelvi szabványba való bevezetésének folyamatáról a specifikációkcímű cikkben olvashat bővebben.
Bajnok-kérdés: https://github.com/dotnet/csharplang/issues/435
Összefoglalás
Nyelvi támogatás natív méretű aláírt és aláíratlan egész számokhoz.
A motiváció az interop forgatókönyvek és az alacsony szintű kódtárak esetében van.
Tervezés
Az azonosítók nint és nuint új környezetfüggő kulcsszavak, amelyek natív aláírt és aláíratlan egész számtípusokat jelölnek.
Az azonosítókat csak akkor kezeli a rendszer kulcsszavakként, ha a névkeresés nem talál életképes eredményt az adott programhelyen.
nint x = 3;
_ = nint.Equals(x, 3);
A nint és a nuint típusokat a mögöttes típusok, System.IntPtr és System.UIntPtr képviselik, a fordító pedig további átalakításokat és műveleteket biztosít ezekhez a típusokhoz, mint natív egész számok.
Állandók
Az állandó kifejezések nint vagy nuinttípusúak lehetnek.
A natív int literálokhoz nincs közvetlen szintaxis. Más integrál állandó értékek implicit vagy explicit vetületei használhatók helyette: const nint i = (nint)42;.
nint állandók az [ int.MinValue, int.MaxValue ] tartományban vannak.
nuint állandók az [ uint.MinValue, uint.MaxValue ] tartományban vannak.
Nincsenek MinValue vagy MaxValue mezők nint vagy nuint, mert a nuint.MinValuekivételével ezek az értékek nem bocsáthatók ki állandóként.
Az állandó összecsukás minden unáris operátor esetében támogatott: { +, -, ~ }, és a bináris operátorok esetében: { +, -, *, /, %, ==, !=, <, <=, >, >=, &, |, ^, <<, >> }.
Az állandó összecsukási műveletek kiértékelése a fordítóplatformtól függetlenül Int32 és UInt32 operandusokkal történik, nem pedig natív intekkel a konzisztens működés érdekében.
Ha a művelet 32 bites állandó értéket eredményez, az állandó összecsukás fordítási időpontban történik.
Ellenkező esetben a művelet futásidőben lesz végrehajtva, és nem tekinthető állandónak.
Konverziók
A nint és a IntPtr, valamint a nuint és a UIntPtrközötti identitáskonvertálás történik.
Az összetett típusok közötti azonossági konverzió csak a natív egész számok és a mögöttes típusok különbségein alapul: tömbök, Nullable<>, konstruált típusok és tuple-ök.
Az alábbi táblázatok a speciális típusok közötti átalakításokat ismertetik.
(Az IL minden átalakításhoz tartalmazza a unchecked és checked környezetek variánsait, ha eltérnek.)
Az alábbi táblázat általános megjegyzései:
-
conv.ua natív egész számra való nulla kiterjesztésű átalakítás,conv.ipedig a natív egész számra való előjeles kiterjesztésű átalakítás. -
checkedés szűkítő környezetei a következők:-
conv.ovf.*asigned to * -
conv.ovf.*.unaunsigned to *
-
-
uncheckedszélesebb környezetei a következők:-
conv.i*signed to *-hez (ahol * a célszélesség) -
conv.u*unsigned to *-hez (ahol * a célszélesség)
-
-
uncheckedszűkítési környezetei a következők:-
conv.i*any to signed *-hez (ahol * a célszélesség) -
conv.u*any to unsigned *-hez (ahol * a célszélesség)
-
Néhány példa:
-
sbyte to nintéssbyte to nuintconv.ihasználnak, mígbyte to nintésbyte to nuintconv.uhasználnak, mert mind . -
nint to byteésnuint to bytehasználja aconv.u1-t, mígnint to sbyteésnuint to sbyteaconv.i1-öt.byte,sbyte,shortésushortesetén a "verem típusa" aint32. Ígyconv.i1gyakorlatilag "alakítva egy előjeles bájttá, majd előjelkiterjesztve int32-re", mígconv.u1gyakorlatilag "alakítva egy előjel nélküli bájttá, majd nullakiterjesztve int32-re". -
checked void* to nintugyanúgy használja aconv.ovf.i.un, mintchecked void* to longconv.ovf.i8.un.
| Operandus | Cél | Konverzió | IL |
|---|---|---|---|
object |
nint |
Kicsomagolás | unbox |
void* |
nint |
PointerToVoid | nop / conv.ovf.i.un |
sbyte |
nint |
ImplicitNumerikus | conv.i |
byte |
nint |
ImplicitNumerikus | conv.u |
short |
nint |
ImplicitNumerikus | conv.i |
ushort |
nint |
ImplicitNumerikus | conv.u |
int |
nint |
ImplicitNumerikus | conv.i |
uint |
nint |
ExplicitNumeric | conv.u / conv.ovf.i.un |
long |
nint |
ExplicitNumeric | conv.i / conv.ovf.i |
ulong |
nint |
ExplicitNumeric | conv.i / conv.ovf.i.un |
char |
nint |
ImplicitNumerikus | conv.u |
float |
nint |
ExplicitNumeric | conv.i / conv.ovf.i |
double |
nint |
ExplicitNumeric | conv.i / conv.ovf.i |
decimal |
nint |
ExplicitNumeric | long decimal.op_Explicit(decimal) conv.i / ... conv.ovf.i |
IntPtr |
nint |
Azonosság | |
UIntPtr |
nint |
Egyik sem | |
object |
nuint |
Kicsomagolás | unbox |
void* |
nuint |
PointerToVoid | nop |
sbyte |
nuint |
ExplicitNumeric | conv.i / conv.ovf.u |
byte |
nuint |
ImplicitNumerikus | conv.u |
short |
nuint |
ExplicitNumeric | conv.i / conv.ovf.u |
ushort |
nuint |
ImplicitNumerikus | conv.u |
int |
nuint |
ExplicitNumeric | conv.i / conv.ovf.u |
uint |
nuint |
ImplicitNumerikus | conv.u |
long |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u |
ulong |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u.un |
char |
nuint |
ImplicitNumerikus | conv.u |
float |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u |
double |
nuint |
ExplicitNumeric | conv.u / conv.ovf.u |
decimal |
nuint |
ExplicitNumeric | ulong decimal.op_Explicit(decimal) conv.u / ... conv.ovf.u.un |
IntPtr |
nuint |
Egyik sem | |
UIntPtr |
nuint |
Azonosság | |
| Felsorolás | nint |
Kifejezett felsorolás | |
| Felsorolás | nuint |
Kifejezett felsorolás |
| Operandus | Cél | Konverzió | IL |
|---|---|---|---|
nint |
object |
Ökölvívás | box |
nint |
void* |
PointerToVoid | nop / conv.ovf.u |
nint |
nuint |
ExplicitNumeric |
conv.u (kihagyható) / conv.ovf.u |
nint |
sbyte |
ExplicitNumeric | conv.i1 / conv.ovf.i1 |
nint |
byte |
ExplicitNumeric | conv.u1 / conv.ovf.u1 |
nint |
short |
ExplicitNumeric | conv.i2 / conv.ovf.i2 |
nint |
ushort |
ExplicitNumeric | conv.u2 / conv.ovf.u2 |
nint |
int |
ExplicitNumeric | conv.i4 / conv.ovf.i4 |
nint |
uint |
ExplicitNumeric | conv.u4 / conv.ovf.u4 |
nint |
long |
ImplicitNumerikus | conv.i8 |
nint |
ulong |
ExplicitNumeric | conv.i8 / conv.ovf.u8 |
nint |
char |
ExplicitNumeric | conv.u2 / conv.ovf.u2 |
nint |
float |
ImplicitNumerikus | conv.r4 |
nint |
double |
ImplicitNumerikus | conv.r8 |
nint |
decimal |
ImplicitNumerikus | conv.i8 decimal decimal.op_Implicit(long) |
nint |
IntPtr |
Azonosság | |
nint |
UIntPtr |
Egyik sem | |
nint |
Felsorolás | Kifejezett felsorolás | |
nuint |
object |
Ökölvívás | box |
nuint |
void* |
PointerToVoid | nop |
nuint |
nint |
ExplicitNumeric |
conv.i(kihagyható) / conv.ovf.i.un |
nuint |
sbyte |
ExplicitNumeric | conv.i1 / conv.ovf.i1.un |
nuint |
byte |
ExplicitNumeric | conv.u1 / conv.ovf.u1.un |
nuint |
short |
ExplicitNumeric | conv.i2 / conv.ovf.i2.un |
nuint |
ushort |
ExplicitNumeric | conv.u2 / conv.ovf.u2.un |
nuint |
int |
ExplicitNumeric | conv.i4 / conv.ovf.i4.un |
nuint |
uint |
ExplicitNumeric | conv.u4 / conv.ovf.u4.un |
nuint |
long |
ExplicitNumeric | conv.u8 / conv.ovf.i8.un |
nuint |
ulong |
ImplicitNumerikus | conv.u8 |
nuint |
char |
ExplicitNumeric | conv.u2 / conv.ovf.u2.un |
nuint |
float |
ImplicitNumerikus | conv.r.un conv.r4 |
nuint |
double |
ImplicitNumerikus | conv.r.un conv.r8 |
nuint |
decimal |
ImplicitNumerikus | conv.u8 decimal decimal.op_Implicit(ulong) |
nuint |
IntPtr |
Egyik sem | |
nuint |
UIntPtr |
Azonosság | |
nuint |
Felsorolás | Kifejezett felsorolás |
Az A-ról Nullable<B>-ra való átalakítás a következő:
- implicit null értékű átalakítás, ha identitásátalakítás vagy implicit átalakítás történik
A-rólB; - explicit null értékű konverzió, ha van explicit konverzió a
A-ről aB. - egyéb esetben érvénytelen.
Az Nullable<A>-ról B-ra való átalakítás a következő:
- explicit null értékű átalakítás, ha identitásátalakítás vagy implicit vagy explicit numerikus átalakítás történik
A-rólB; - egyéb esetben érvénytelen.
Az Nullable<A>-ról Nullable<B>-ra való átalakítás a következő:
- identitáskonvertálás, ha létezik egy identitáskonvertálás
A-rólB-re; - explicit nullable konverzió, ha implicit vagy explicit numerikus átalakítás történik
A-rőlB; - egyéb esetben érvénytelen.
Üzemeltetők
Az előre definiált operátorok a következők.
Ezeket az operátorokat az implicit átalakításokra vonatkozó normál szabályok alapján veszik figyelembe a túlterhelés feloldása során , ha az operandusok közül legalább az egyik nint vagy nuinttípusú.
(Az il minden operátorhoz tartalmazza a unchecked és checked környezetek variánsait, ha eltérnek.)
| Unary | Operátor aláírása | 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áris | Operátor aláírása | 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 |
Egyes bináris operátorok esetében az IL operátorok további operandustípusokat támogatnak (lásd ECMA-335 III.1.5 Operandus típusú táblázatot). A C# által támogatott operandustípusok azonban az egyszerűség és a nyelv meglévő operátoraival való konzisztenciára korlátozódnak.
Az operátorok feloldott verziói, ahol az argumentumok és a visszatérési típusok nint? és nuint?, támogatottak.
Az összetett hozzárendelési műveletek x op= y, amelyekben x vagy y natív intsek, ugyanazokat a szabályokat követik, mint más, előre definiált operátorokkal rendelkező primitív típusok esetében.
Pontosabban a kifejezés x = (T)(x op y), ahol T a x típusa, és ahol a x csak egyszer értékelődik ki.
A shift operátoroknak el kell maszkolnia az eltoláshoz szükséges bitek számát – 5 bitre, ha sizeof(nint) 4, és 6 bitre, ha sizeof(nint) 8.
(lásd §12.11) a C# specifikációban).
A C#9 fordító hibajelentést tesz az előre definiált natív egész számok operátoraihoz való kötésről egy korábbi nyelvi verzió összeállításakor, de lehetővé teszi a natív egész számokra és azokból származó előre definiált konverziók használatát.
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
}
}
Mutatók aritmetikája
A C#-ban nincsenek előre definiált operátorok a mutató hozzáadásához vagy kivonásához natív egész szám eltolásokkal.
Ehelyett a nint és nuint értékeket long és ulong értékekre léptetik elő, és a mutató aritmetika előre definiált operátorokat használ ezekhez a típusokhoz.
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áris numerikus előléptetések
A bináris numerikus promóciók informatív szövege (lásd a §12.4.7.3a C# specifikáció szerint) a következőképpen frissül:
- …
- Ellenkező esetben, ha bármelyik operandus
ulongtípusú, a másik operandusulongtípussá alakul, vagy kötési idő hiba történik, ha a másik operandussbyte,short,int,nintvagylongtípusú.- Ellenkező esetben, ha bármelyik operandus
nuinttípusú, a másik operandusnuinttípussá alakul, vagy kötési idő hibát tapasztal, ha a másik operandussbyte,short,int,nintvagylongtípusú.- Ellenkező esetben, ha bármelyik operandus
longtípusú, a másik operanduslongtípussá alakul.- Ellenkező esetben, ha bármelyik operandus
uinttípusú, a másik operandus pedigsbyte,short,nint, vagyinttípusú, akkor mindkét operanduslongtípussá alakul.- Ellenkező esetben, ha bármelyik operandus
uinttípusú, a másik operandusuinttípussá alakul.- Ellenkező esetben, ha bármelyik operandus
ninttípusú, a másik operandusninttípussá alakul.- Ellenkező esetben mindkét operandus
inttípussá lesz konvertálva.
Dinamikus
Az átalakításokat és operátorokat a fordító szintetizálja, és nem része az alapul szolgáló IntPtr és UIntPtr típusnak.
Ennek következtében ezek a konverziók és operátorok nem érhetők el a dynamicfuttatókörnyezeti iratgyűjtőjéből.
nint x = 2;
nint y = x + x; // ok
dynamic d = x;
nint z = d + x; // RuntimeBinderException: '+' cannot be applied 'System.IntPtr' and 'System.IntPtr'
Tagok beírása
A nint vagy nuint egyetlen konstruktora a paraméter nélküli konstruktor.
A System.IntPtr és System.UIntPtralábbi tagjai kifejezetten ki vannak zárvanint-ből vagy nuint-ből:
// 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();
A System.IntPtr és System.UIntPtrfennmaradó tagjai implicit módonnint és nuint. .NET-keretrendszer 4.7.2 esetén:
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
public string ToString(string format);
A System.IntPtr és System.UIntPtráltal implementált interfészek implicit módon szerepelnek nint és nuintközött, ahol az alapul szolgáló típusok helyett a megfelelő natív egész típusok találhatók.
Ha például IntPtr implementálja ISerializable, IEquatable<IntPtr>, IComparable<IntPtr>, akkor nint implementálja ISerializable, IEquatable<nint>, IComparable<nint>.
Felülbírálás, elrejtés és megvalósítás
nint és System.IntPtr, valamint nuint és System.UIntPtregyenértékűnek minősülnek a felülbírálás, elrejtés és végrehajtás szempontjából.
A túlterhelések nem különbözhetnek meg csak nint és System.IntPtr, valamint nuint és System.UIntPtralapján.
A felülbírálások és implementációk eltérhetnek kizárólag a nint és System.IntPtr, vagy a nuint és System.UIntPtralapján.
A metódusok elrejtik azokat a metódusokat, amelyek kizárólag nint és System.IntPtr, vagy nuint és System.UIntPtralapján különböznek.
Vegyes
nint és nuint tömbindexként használt kifejezések konvertálás nélkül lesznek kibocsátva.
static object GetItem(object[] array, nint index)
{
return array[index]; // ok
}
nint és nuint nem használható enum alaptípusként a C#-ból.
enum E : nint // error: byte, sbyte, short, ushort, int, uint, long, or ulong expected
{
}
Az olvasások és írások nint és nuintesetén atomiak.
A mezők volatile jelölhetők a nint és nuinttípusok esetén.
ECMA-334 15.5.4 azonban nem tartalmazza az enumSystem.IntPtr vagy System.UIntPtr alaptípusokat.
default(nint) és new nint() egyenértékűek a (nint)0; default(nuint) és new nuint() egyenértékűek a (nuint)0.
typeof(nint)
typeof(IntPtr); typeof(nuint)typeof(UIntPtr).
A sizeof(nint) és a sizeof(nuint) támogatottak, de nem biztonságos környezetben történő összeállítást igényelnek (sizeof(IntPtr) és sizeof(UIntPtr)).
Az értékek nem fordítási időállandók.
A sizeof(nint)sizeof(IntPtr)-ként valósul meg, nem pedig IntPtr.Size-ként; A sizeof(nuint)sizeof(UIntPtr)-ként valósul meg, nem pedig UIntPtr.Size-ként.
A típushivatkozások fordítódiagnosztikái nint vagy nuint esetében nint-t vagy nuint-at jelentenek, nem pedig IntPtr-et vagy UIntPtr-öt.
Metaadatok
nint és nuintSystem.IntPtr és System.UIntPtrmetaadatokban jelennek meg.
A nint vagy nuint tartalmazó típushivatkozásokat egy System.Runtime.CompilerServices.NativeIntegerAttribute bocsátja ki, amely jelzi, hogy a típushivatkozás mely részei natív intsek.
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;
}
}
A típushivatkozások NativeIntegerAttribute kódolását a NativeIntegerAttribute.mdismerteti.
Alternatívák
A fenti "típustörlés" módszer alternatíva az új típusok bevezetése: System.NativeInt és System.NativeUInt.
public readonly struct NativeInt
{
public IntPtr Value;
}
A különböző típusok lehetővé tennék a IntPtr-tól eltérő túlterhelést, valamint a különböző elemzések és ToString()használatát.
De több munka lenne a CLR számára, hogy hatékonyan kezelje ezeket a típusokat, ami legyőzi a funkció elsődleges célját - a hatékonyságot.
A IntPtr használó natív int-kódokkal való együttműködés pedig nehezebb lenne.
Egy másik lehetőség, hogy további natív integer támogatás bővítését adjuk hozzá a IntPtr keretrendszerhez, konkrét fordítói támogatás nélkül.
A fordító automatikusan támogatja az új konverziókat és aritmetikai műveleteket.
A nyelv azonban nem biztosít kulcsszavakat, állandókat vagy checked műveleteket.
Tervezési értekezletek
- 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