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.
Bajnoki probléma: https://github.com/dotnet/csharplang/issues/7431
Összefoglalás
Általános célú és biztonságos mechanizmust biztosít a InlineArrayAttribute funkciót használó szerkezettípusok használatára. Biztosítson egy általános célú és biztonságos mechanizmust a beágyazott tömbök C#-osztályokon, szerkezeteken és interfészeken belüli deklarálására.
Megjegyzés: A specifikáció korábbi verziói a "ref-safe-to-escape" és a "safe-to-escape" kifejezéseket használták, amelyek a Span biztonsági funkció specifikációjában lettek bevezetve. A ECMA szabványbizottsága a neveket "ref-safe-context", illetve "safe-context"névre módosította. A biztonságos környezet értékeit a "deklarációs blokk", a "függvény tag" és a "hívói környezet" következetes használata érdekében pontosítottuk. A specifikációk különböző kifejezéseket használtak ezekhez a kifejezésekhez, és a "safe-to-return" kifejezést is használták a "hívó-kontextus" szinonimájaként. Ez a specifikáció a C# 7.3 szabványban szereplő kifejezések használatára lett frissítve.
Motiváció
Ez a javaslat a https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/unsafe-code.md#238-fixed-size-buffersszámos korlátozását tervezi kezelni . Konkrétan a következő célokra törekszik:
- a InlineArrayAttribute funkciót használó szerkezettípusok elemeinek elérése;
- a felügyelt és nem felügyelt típusok soros tömbjeinek deklarációja egy
struct,classvagyinterface-ben.
És biztosítson nyelvbiztonsági ellenőrzést számukra.
Részletes tervezés
A futtatási környezet nemrég hozzáadta az InlineArrayAttribute funkciót. Röviden: a felhasználó az alábbihoz hasonló struktúratípust deklarálhat:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
private object _element0;
}
A futtatókörnyezet speciális típuselrendezést biztosít a Buffer típushoz:
- A típus méretét úgy bővítjük, hogy 10 elem férjen el (a szám az InlineArray attribútumból származik)
objecttípusból. (A típus a struktúra egyetlen példánymezőjének típusából származik, ebben a példában_element0.) - Az első elem a példánymezőhöz és a szerkezet elejéhez igazodik
- Az elemek egymás után vannak elhelyezve a memóriában, mintha egy tömb elemei lennének.
A futtatókörnyezet rendszeres GC-nyomkövetést biztosít a szerkezet összes eleméhez.
Ez a javaslat az ehhez hasonló típusokat "beágyazott tömbtípusokként" fogja hivatkozni.
A soros tömbtípus elemei mutatókkal vagy a System.Runtime.InteropServices.MemoryMarshal.CreateSpan/System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpan API-k által visszaadott span példányok segítségével érhetők el. Azonban sem a mutató megközelítése, sem az API-k nem biztosítanak automatizált típust és határérték-ellenőrzést.
A nyelv típusbiztos/ref-biztonságos módot biztosít a beágyazott tömbtípusok elemeinek eléréséhez. A hozzáférés kiterjedésalapú lesz. Ez korlátozza a beágyazott tömbtípusok támogatását olyan elemtípusokkal, amelyek típusargumentumként használhatók. Például a mutatótípus nem használható elemtípusként. Más példák a kiterjedés típusaira.
Soros tömbtípushoz tartozó spantípusok példányainak beszerzése
Mivel garantálható, hogy a beágyazott tömbtípus első eleme a típus elejéhez van igazítva (nincs rés), a fordító a következő kódot fogja használni egy Span érték lekéréséhez:
MemoryMarshal.CreateSpan(ref Unsafe.As<TBuffer, TElement>(ref buffer), size);
És a következő kód egy ReadOnlySpan érték lekéréséhez:
MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<TBuffer, TElement>(ref Unsafe.AsRef(in buffer)), size);
A használatban lévő il-méret csökkentése érdekében a fordítónak képesnek kell lennie arra, hogy két általános, újrafelhasználható segédet vegyen fel a privát megvalósítás részleteinek típusába, és használja őket az ugyanazon program összes felhasználási helyén.
public static System.Span<TElement> InlineArrayAsSpan<TBuffer, TElement>(ref TBuffer buffer, int size) where TBuffer : struct
{
return MemoryMarshal.CreateSpan(ref Unsafe.As<TBuffer, TElement>(ref buffer), size);
}
public static System.ReadOnlySpan<TElement> InlineArrayAsReadOnlySpan<TBuffer, TElement>(in TBuffer buffer, int size) where TBuffer : struct
{
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<TBuffer, TElement>(ref Unsafe.AsRef(in buffer)), size);
}
Elem hozzáférés
Az elemhozzáférés ki lesz terjesztve a beágyazott tömbelemek elérésének támogatására.
A element_access egy primary_no_array_creation_expression-ból, egy „[” tokenből, egy argument_list-ból és egy „]” tokenből áll. A argument_list egy vagy több argumentumbóls-ből áll, vesszővel elválasztva.
element_access
: primary_no_array_creation_expression '[' argument_list ']'
;
A element_accessargument_list nem tartalmazhatnak ref vagy out argumentumokat.
Dinamikusan kötött egy element_access (§11.3.3), ha az alábbiak közül legalább az egyik fennáll:
- A primary_no_array_creation_expression fordítási időben meghatározott típusú
dynamic. - A argument_list legalább egy kifejezése fordításidejű típusú
dynamic, és a elsődleges_nem_tömb_létrehozási_expr nem rendelkezik tömbtípussal, és a elsődleges_nem_tömb_létrehozási_expr nem rendelkezik beágyazott tömbtípussal, vagy az argumentumlistában több elem is szerepel.
Ebben az esetben a fordító a element_accessdynamictípusú értékként sorolja be. Az alábbi szabályokat futásidőben alkalmazzák a element_access jelentésének meghatározása céljából, a futásidő típusát használva a primary_no_array_creation_expression és argument_list kifejezések fordítási idő típusa helyett, melyeknek fordítási idő típusa dynamic. Ha a primary_no_array_creation_expression nem rendelkezik fordítási időpontbeli típusú dynamic, akkor az elemhozzáférés korlátozott fordítási idejű ellenőrzésen megy keresztül a §11.6.5-ben leírtak szerint.
Ha a(z) element_access egy array_typeértéke egy primary_no_array_creation_expression, akkor a(z) element_access tömbhozzáférés (§12.8.12.2). Ha egy element_accessprimary_no_array_creation_expression egy beágyazott tömbtípus változója vagy értéke, és a argument_list egyetlen argumentumból áll, a element_access beágyazott tömbelem-hozzáférés. Ellenkező esetben a primary_no_array_creation_expression egy egy vagy több indexelőtaggal rendelkező osztály, szerkezet vagy interfésztípus változója vagy értéke, amely esetben a element_access indexelő hozzáférés (§12.8.12.3).
Beágyazott tömbelem-hozzáférés
Beágyazott tömbelem-hozzáférés esetén a element_accessprimary_no_array_creation_expression egy beágyazott tömbtípus változójának vagy értékének kell lennie. Ezenkívül a beágyazott tömbelem-hozzáférés argument_list nem tartalmazhat nevesített argumentumokat. A argument_list egyetlen kifejezést kell tartalmaznia, és a kifejezésnek
-
inttípusú, vagy - implicit módon átalakítható
int, vagy - implicit módon átalakítható
System.Index, vagy - implicit módon átalakítható
System.Range-ba.
Amikor a kifejezés típusa int
Ha a primary_no_array_creation_expression írható változó, akkor a beágyazott tömbelem-elérés kiértékelésének eredménye egy írható változó, amely egyenértékű azzal, hogy a public ref T this[int index] { get; } hívást végrehajtjuk az adott egész értékkel a System.Span<T> metódus által visszaadott System.Span<T> InlineArrayAsSpan példányon a primary_no_array_creation_expressionesetén. A ref-biztonsági elemzés céljából a ref-safe-context/és a biztonságos hozzáférési környezet egyenértékűek egy olyan metódus meghívásával, amelynek aláírása static ref T GetItem(ref InlineArrayType array).
Az eredményként kapott változó akkor és csak akkor tekinthető ingónak, ha primary_no_array_creation_expression ingó.
Ha a primary_no_array_creation_expression egy írásvédett változó, akkor a beágyazott tömbelem-hozzáférés kiértékelésének eredménye egy írásvédett változó, amely egyenértékű a public ref readonly T this[int index] { get; } meghívásával. Ez a meghívás az System.ReadOnlySpan<T>System.ReadOnlySpan<T> InlineArrayAsReadOnlySpan metódusa által visszaadott egy példányán található, és egy adott egész szám értékét adja vissza. A ref-biztonsági elemzés céljából a ref-safe-context/és a biztonságos hozzáférési környezet egyenértékűek egy olyan metódus meghívásával, amelynek aláírása static ref readonly T GetItem(in InlineArrayType array).
Az eredményként kapott változó akkor és csak akkor tekinthető ingónak, ha primary_no_array_creation_expression ingó.
Ha a primary_no_array_creation_expression érték, akkor a beágyazott tömbelem-hozzáférés kiértékelésének eredménye olyan érték, amely egyenértékű azzal, hogy a public ref readonly T this[int index] { get; }System.ReadOnlySpan<T> metódusa által visszaadott System.ReadOnlySpan<T> InlineArrayAsReadOnlySpan példányán meghívjuk a metódust azzal az egész szám értékkel. A ref-biztonsági elemzés céljából a ref-safe-context/és a biztonságos hozzáférési környezet egyenértékűek egy olyan metódus meghívásával, amelynek aláírása static T GetItem(InlineArrayType array).
Például:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer10<T>
{
private T _element0;
}
void M1(Buffer10<int> x)
{
ref int a = ref x[0]; // Ok, equivalent to `ref int a = ref InlineArrayAsSpan<Buffer10<int>, int>(ref x, 10)[0]`
}
void M2(in Buffer10<int> x)
{
ref readonly int a = ref x[0]; // Ok, equivalent to `ref readonly int a = ref InlineArrayAsReadOnlySpan<Buffer10<int>, int>(in x, 10)[0]`
ref int b = ref x[0]; // An error, `x` is a readonly variable => `x[0]` is a readonly variable
}
Buffer10<int> GetBuffer() => default;
void M3()
{
int a = GetBuffer()[0]; // Ok, equivalent to `int a = InlineArrayAsReadOnlySpan<Buffer10<int>, int>(GetBuffer(), 10)[0]`
ref readonly int b = ref GetBuffer()[0]; // An error, `GetBuffer()[0]` is a value
ref int c = ref GetBuffer()[0]; // An error, `GetBuffer()[0]` is a value
}
A beágyazott tömbök deklarált tömbhatárokon kívüli állandó kifejezéssel rendelkező beágyazott tömbbe való indexelés fordítási időhiba.
Amikor a kifejezés implikáltan átalakítható int-vá
A kifejezés intre lesz konvertálva, majd az elemhozzáférés az Amikor a kifejezéstípus int szakaszban leírtak szerint lesz értelmezve.
Amikor a kifejezés implicit módon átalakítható System.Index
A kifejezés System.Indexlesz átalakítva, amely ezután int-alapú indexértékké alakul át a https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/ranges.md#implicit-index-supportleírtak szerint, feltéve, hogy a gyűjtemény hossza fordításkor ismert, és megegyezik a primary_no_array_creation_expressionbeágyazott tömbtípusának elemeinek mennyiségével. Ezután az elemhozzáférés az "A kifejezéstípus int" szakaszban leírtak szerint lesz értelmezve.
Amikor a kifejezés implicit módon átalakítható System.Range
Ha a primary_no_array_creation_expression írható változó, akkor a beágyazott tömbelem-hozzáférés kiértékelésének eredménye egy érték, amely egyenértékű a public Span<T> Slice (int start, int length)által használt System.Span<T> metódussal visszaadott System.Span<T> InlineArrayAsSpan egy példányán végrehajtott meghívásával.
A ref-biztonsági elemzés céljából a ref-safe-context/és a biztonságos hozzáférési környezet egyenértékűek egy olyan metódus meghívásával, amelynek aláírása static System.Span<T> GetSlice(ref InlineArrayType array).
Ha a primary_no_array_creation_expression csak olvasható változó, akkor a beágyazott tömbelem-hozzáférés kiértékelésének eredménye egy olyan érték, amely ekvivalens a public ReadOnlySpan<T> Slice (int start, int length) meghívásával egy System.ReadOnlySpan<T> példányán, amit a System.ReadOnlySpan<T> InlineArrayAsReadOnlySpan metódusa ad vissza.
A ref-biztonsági elemzés céljából a ref-safe-context/és a biztonságos hozzáférési környezet egyenértékűek egy olyan metódus meghívásával, amelynek aláírása static System.ReadOnlySpan<T> GetSlice(in InlineArrayType array).
Ha a primary_no_array_creation_expression egy érték, akkor hibaüzenet jelenik meg.
A Slice metódus meghívásának argumentumait a System.Rangeszakaszban ismertetett https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/ranges.md#implicit-range-support konvertált indexkifejezésből számítjuk ki, feltéve, hogy a gyűjtemény hossza fordításkor ismert, és megegyezik a primary_no_array_creation_expressionbeágyazott tömbtípusának elemeinek mennyiségével.
A fordító kihagyhatja a Slice hívást, ha a fordításkor ismert, hogy start 0, és length kisebb vagy egyenlő a beágyazott tömbtípus elemeinek mennyiségével. A fordító akkor is jelenthet hibát, ha fordítás közben ismert, hogy a szeletelés túllépi a soros tömb határait.
Például:
void M1(Buffer10<int> x)
{
System.Span<int> a = x[..]; // Ok, equivalent to `System.Span<int> a = InlineArrayAsSpan<Buffer10<int>, int>(ref x, 10).Slice(0, 10)`
}
void M2(in Buffer10<int> x)
{
System.ReadOnlySpan<int> a = x[..]; // Ok, equivalent to `System.ReadOnlySpan<int> a = InlineArrayAsReadOnlySpan<Buffer10<int>, int>(in x, 10).Slice(0, 10)`
System.Span<int> b = x[..]; // An error, System.ReadOnlySpan<int> cannot be converted to System.Span<int>
}
Buffer10<int> GetBuffer() => default;
void M3()
{
_ = GetBuffer()[..]; // An error, `GetBuffer()` is a value
}
Konverziók
A rendszer hozzáad egy új átalakítást, egy beágyazott tömbkonverziót a kifejezésből. A beágyazott tömb konvertálása szabványos konverzió.
Implicit átalakítás történik egy beágyazott tömbtípus kifejezéséből a következő típusokra:
System.Span<T>System.ReadOnlySpan<T>
Azonban hiba, ha egy csak olvasható változót System.Span<T>-ra alakítanak át, vagy egy értéket bármelyik típusra konvertálnak.
Például:
void M1(Buffer10<int> x)
{
System.ReadOnlySpan<int> a = x; // Ok, equivalent to `System.ReadOnlySpan<int> a = InlineArrayAsReadOnlySpan<Buffer10<int>, int>(in x, 10)`
System.Span<int> b = x; // Ok, equivalent to `System.Span<int> b = InlineArrayAsSpan<Buffer10<int>, int>(ref x, 10)`
}
void M2(in Buffer10<int> x)
{
System.ReadOnlySpan<int> a = x; // Ok, equivalent to `System.ReadOnlySpan<int> a = InlineArrayAsReadOnlySpan<Buffer10<int>, int>(in x, 10)`
System.Span<int> b = x; // An error, readonly mismatch
}
Buffer10<int> GetBuffer() => default;
void M3()
{
System.ReadOnlySpan<int> a = GetBuffer(); // An error, ref-safety
System.Span<int> b = GetBuffer(); // An error, ref-safety
}
A ref-safety elemzés céljából az átalakítás biztonságos környezete egyenértékű azzal, mintha egy biztonságos környezetben végrehajtott metódushívás történne, amelynek static System.Span<T> Convert(ref InlineArrayType array)vagy static System.ReadOnlySpan<T> Convert(in InlineArrayType array)aláírása van.
Listaminták
Listaminták nem lesznek támogatottak a beágyazott tömbtípusok esetén.
Határozott hozzárendelés-ellenőrzés
A normál határozott hozzárendelési szabályok beágyazott tömbtípusú változókra vonatkoznak.
Gyűjteményliterálok
A beágyazott tömbtípus egy példánya érvényes kifejezésként szerepel egy spread_elementsorán.
Az alábbi funkció nem a C# 12-ben lett elküldve. Ez továbbra is nyílt javaslat marad. A példában szereplő kód CS9174hoz létre:
A beágyazott tömbtípus érvényes konstrukciós gyűjtemény céltípus egy gyűjteménykifejezéshez. Például:
Buffer10<int> b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // initializes user-defined inline array
A gyűjteménykonstans hosszának meg kell egyeznie a célzott tömbtípus hosszával. Ha a literál hossza a fordításkor ismert, és nem egyezik a cél hosszával, hibaüzenet jelenik meg. Ellenkező esetben a rendszer kivételt fog kivenni futásidőben, ha az eltérést tapasztalja. A pontos kivételtípus a TBD. Néhány jelölt: System.NotSupportedException, System.InvalidOperationException.
Az InlineArrayAttribute alkalmazások érvényesítése
A Fordító az InlineArrayAttribute alkalmazások alábbi aspektusait ellenőrzi:
- A céltípus egy nem rekord típusú struktúra
- A céltípus csak egy mezővel rendelkezik
- Megadott hossz > 0
- A célstruktúra nem rendelkezik explicit elrendezéssel
Beágyazott tömbelemek egy objektum inicializálójában
Alapértelmezés szerint az elem inicializálása nem támogatott az űrlap '[' argument_list ']' keresztül (lásd https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#128173-object-initializers):
static C M2() => new C() { F = {[0] = 111} }; // error CS1913: Member '[0]' cannot be initialized. It is not a field or property.
class C
{
public Buffer10<int> F;
}
Ha azonban a beágyazott tömbtípus explicit módon definiálja a megfelelő indexelőt, az objektuminicializáló a következőt fogja használni:
static C M2() => new C() { F = {[0] = 111} }; // Ok, indexer is invoked
class C
{
public Buffer10<int> F;
}
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer10<T>
{
private T _element0;
public T this[int i]
{
get => this[i];
set => this[i] = value;
}
}
A foreach utasítás
A foreach utasítás úgy lesz módosítva, hogy lehetővé tegye a beágyazott tömbtípus használatát gyűjteményként egy foreach utasításban.
Például:
foreach (var a in getBufferAsValue())
{
WriteLine(a);
}
foreach (var b in getBufferAsWritableVariable())
{
WriteLine(b);
}
foreach (var c in getBufferAsReadonlyVariable())
{
WriteLine(c);
}
Buffer10<int> getBufferAsValue() => default;
ref Buffer10<int> getBufferAsWritableVariable() => default;
ref readonly Buffer10<int> getBufferAsReadonlyVariable() => default;
egyenértékű:
Buffer10<int> temp = getBufferAsValue();
foreach (var a in (System.ReadOnlySpan<int>)temp)
{
WriteLine(a);
}
foreach (var b in (System.Span<int>)getBufferAsWritableVariable())
{
WriteLine(b);
}
foreach (var c in (System.ReadOnlySpan<int>)getBufferAsReadonlyVariable())
{
WriteLine(c);
}
Támogatni fogjuk a foreach át a beágyazott tömbök felett, még akkor is, ha az async módszerekben korlátozottként indítjuk, a span típusok bevonása miatt a fordításba.
Tervezési kérdések megnyitása
Alternatívák
Soros tömbtípus szintaxisa
A https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/types.md#821-general nyelvhelyességi beállítása az alábbiak szerint történik:
array_type
: non_array_type rank_specifier+
;
rank_specifier
: '[' ','* ']'
+ | '[' constant_expression ']'
;
A constant_expression típusának implicit módon konvertálhatónak kell lennie inttípussá, és az értéknek nem nulla pozitív egész számnak kell lennie.
A https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/arrays.md#1721-general szakasz vonatkozó részét az alábbiak szerint módosítjuk.
A tömbtípusok nyelvi szabályai a https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/types.md#821-general-ban vannak megadva.
A tömbtípus non_array_type formában van megadva, amelyet egy vagy több rank_specifierkövet.
A non_array_type olyan típus, amely nem önmagában array_type.
A tömbtípus rangját a array_typebal szélső rank_specifier adja meg: A rank_specifier azt jelzi, hogy a tömb rangja egy plusz a ,"" tokenek számával egyenlő.
A tömbtípus elemtípusa az a típus, amely a bal szélső rank_specifiertörlését eredményezi:
- A
T[ constant_expression ]formájú tömbtípus egy névtelen beágyazott tömbtípus, amelynek hossza konstans kifejezés és nem tömb típusú elemT. - Az űrlap
T[ constant_expression ][R₁]...[Rₓ]tömbtípusa névtelen beágyazott tömbtípus, amelynek hosszát constant_expression jelöli, és egy elemtípustT[R₁]...[Rₓ]. - Az
T[R]formájú tömbtípus (ahol az R nem konstans_kifejezés) egy normál tömbtípus, amelynek rangjaRés elemtípusa nem tömbT. - A
T[R][R₁]...[Rₓ]típusú tömb (ahol az R nem konstans_kifejezés) egy normál tömbtípus, amelynek elemtípusaRés rangjaT[R₁]...[Rₓ]van.
Gyakorlatilag a rank_specifier-eket balról jobbra olvassuk , mielőtt elérnénk a végső nem tömb elemtípust.
példa: A
int[][,,][,]típusa egy egydimenziós tömb, amely háromdimenziós tömbökből áll, amelyekintkétdimenziós tömbjeit tartalmazzák. példa vége
Futásidőben egy normál tömbtípus értéke null vagy az adott tömbtípus egy példányára való hivatkozás lehet.
Megjegyzés: A https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/arrays.md#176-array-covarianceszabályainak követésével az érték egy kovariantikus tömbtípusra is hivatkozhat. végjegyzet
A névtelen beágyazott tömbtípus egy fordító által szintetizált inline tömbtípus, belső elérhetőséggel. Az elemtípusnak olyan típusnak kell lennie, amely típusargumentumként használható. A explicit módon deklarált beágyazott tömbtípustól eltérően egy névtelen beágyazott tömbtípusra nem lehet név alapján hivatkozni, csak array_type szintaxissal hivatkozhat rá. Ugyanazon program kontextusában bármely két azonos elemtípusú és azonos hosszúságú array_typebeágyazott tömbtípus ugyanarra a névtelen beágyazott tömbtípusra vonatkozik.
A belső akadálymentesség mellett a fordító megakadályozza, hogy az API-k névtelen beágyazott tömbtípusokat használjanak a szerelvényhatárok között egy szükséges egyéni módosító (pontos típusú TBD) használatával, amelyet egy névtelen beágyazott tömbtípus-referencia alkalmaz az aláírásban.
Tömblétrehozó kifejezések
array_creation_expression
: 'new' non_array_type '[' expression_list ']' rank_specifier*
array_initializer?
| 'new' array_type array_initializer
| 'new' rank_specifier array_initializer
;
A jelenlegi nyelvhelyesség alapján a expression_list helyett egy constant_expression használata már azt jelenti, hogy a megadott hosszúságú normál egydimenziós tömbtípust kell kiosztani. Ezért array_creation_expression továbbra is egy normál tömb lefoglalását jelenti.
A rank_specifier új formájával azonban egy névtelen beágyazott tömbtípust is beépíthet a lefoglalt tömb elemtípusába.
Az alábbi kifejezések például egy 2 elem hosszúságú rendes tömböt hoznak létre, melynek elemtípusa egy névtelen beágyazott tömbtípus, int típusú és 5 elem hosszúságú.
new int[2][5];
new int[][5] {default, default};
new [] {default(int[5]), default(int[5])};
Tömb inicializálói
A tömb inicializálói nem lettek implementálva a C# 12-ben. Ez a szakasz továbbra is aktív javaslat marad.
A Tömbinicializálók szakasz úgy lesz beállítva, hogy lehetővé tegye a array_initializer használatát a beágyazott tömbtípusok inicializálásához (nincs szükség a nyelvhelyesség módosítására).
array_initializer
: '{' variable_initializer_list? '}'
| '{' variable_initializer_list ',' '}'
;
variable_initializer_list
: variable_initializer (',' variable_initializer)*
;
variable_initializer
: expression
| array_initializer
;
A beágyazott tömb hosszát a céltípusnak explicit módon kell megadnia.
Például:
int[5] a = {1, 2, 3, 4, 5}; // initializes anonymous inline array of length 5
Buffer10<int> b = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // initializes user-defined inline array
var c = new int[][] {{11, 12}, {21, 22}, {31, 32}}; // An error for the nested array initializer
var d = new int[][2] {{11, 12}, {21, 22}, {31, 32}}; // An error for the nested array initializer
Részletes tervezés (2. lehetőség)
Vegye figyelembe, hogy a javaslat részében a "rögzített méretű puffer" kifejezés a javasolt "biztonságos rögzített méretű puffer" funkcióra utal, nem pedig a https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/unsafe-code.md#238-fixed-size-buffers-nál leírt pufferre.
Ebben a kialakításban a rögzített méretű puffertípusok nem kapnak általános speciális kezelést a nyelv által. Egy speciális szintaxissal deklarálhatók a rögzített méretű puffereket képviselő tagok, és új szabályok vonatkoznak a tagok felhasználására. Ezek nem nyelvi szempontból mezők.
A https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#155-fields nyelvhelyessége ki lesz bővítve a puffer méretének meghatározásához:
field_declaration
: attributes? field_modifier* type variable_declarators ';'
;
field_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'static'
| 'readonly'
| 'volatile'
| unsafe_modifier // unsafe code support
;
variable_declarators
: variable_declarator (',' variable_declarator)*
;
variable_declarator
: identifier ('=' variable_initializer)?
+ | fixed_size_buffer_declarator
;
fixed_size_buffer_declarator
: identifier '[' constant_expression ']'
;
A fixed_size_buffer_declarator egy adott elemtípusú rögzített méretű puffer bevezetésére szolgál.
A pufferelem típusa a típus, amely a field_declaration-ban van meghatározva. A rögzített méretű pufferdeklarátor egy tag nevét tartalmazó azonosítóval új tagot vezet be, amelyet egy, a [ és ] jelzők által zárójelben lévő állandó kifejezés követ. Az állandó kifejezés a rögzített méretű pufferdeklarátor által bevezetett tag elemeinek számát jelöli. Az állandó kifejezés típusának implicit módon konvertálhatónak kell lennie inttípussá, az értéknek pedig nem nulla pozitív egész számnak kell lennie.
A rögzített méretű puffer elemeit egymás után kell elhelyezni a memóriában, mintha tömb elemei lennének.
Egy field_declarationfixed_size_buffer_declarator egy felületen static módosítóval kell rendelkeznie.
A helyzettől függően (a részleteket alább találja) a rögzített méretű puffertaghoz való hozzáférés System.ReadOnlySpan<S> vagy System.Span<S>értékének (soha nem változónak) minősül, ahol az S a rögzített méretű puffer elemtípusa. Mindkét típus olyan indexelőket biztosít, amelyek egy adott elemre mutató hivatkozást adnak vissza megfelelő "olvashatósággal", ami megakadályozza az elemekhez való közvetlen hozzárendelést, ha a nyelvi szabályok ezt nem teszik lehetővé.
Ez a rögzített méretű pufferelem-típusként használható típuskészletet olyan típusokra korlátozza, amelyek típusargumentumként használhatók. Például a mutatótípus nem használható elemtípusként.
Az eredményként kapott span példány hossza megegyezik a rögzített méretű pufferen deklarált méretével. A megadott rögzített méretű pufferkorlátokon kívül álló állandó kifejezéssel való indexelés fordítási időhiba.
Az érték biztonságos környezete megegyezik a tároló biztonságos környezetével, ahogyan az akkor is lenne, ha a háttéradatokat mezőként érnék el.
Rögzített méretű pufferek a kifejezésekben
A rögzített méretű puffertagok tagkeresése pontosan úgy halad, mint egy mező tagkeresése.
Egy rögzített méretű pufferre egy kifejezésben a simple_name vagy a member_access segítségével lehet hivatkozni.
Ha egy példány rögzített méretű puffertagra egyszerű névként hivatkozik, az effektus megegyezik a this.Ialakú taghozzáféréssel, ahol I a rögzített méretű puffertag. Ha egy statikus rögzített méretű puffertagra egyszerű névként hivatkozik, az effektus megegyezik az űrlap E.Itaghozzáférésével, ahol I a rögzített méretű puffertag, E pedig a deklarálási típus.
Nem olvashatóan rögzített méretű pufferek
Az űrlap E.Itaghozzáférésében, ha E egy strukturált típus, és a I tagkeresése ebben a struktúratípusban azonosítja a nem olvasható példány rögzített méretű tagját, akkor a E.I kiértékelése és besorolása az alábbiak szerint történik:
- Ha
Eértékként van besorolva, akkorE.Icsak primary_no_array_creation_expression használható elemhozzáférés részeként,System.Indextípusú vagy implicit módon int típusra átalakítható indexkel. Az elemhozzáférés eredménye egy rögzített méretű tag eleme a megadott helyen, ami értékként van besorolva. - Ellenkező esetben, ha
Eírásvédett változóként van besorolva, és a kifejezés eredményeSystem.ReadOnlySpan<S>típusú értékként van besorolva , ahol S aIelemtípusa . Az érték a tagok elemeinek eléréséhez használható. - Ellenkező esetben a
Eírható változóként van besorolva, a kifejezés eredménye pedigSystem.Span<S>típusú értékként van besorolva , ahol S aIelemtípusa. Az érték a tagok elemeinek eléréséhez használható.
Az űrlap E.Itaghozzáférésében, ha E osztálytípusú, és az adott osztálytípusban I tagkeresése azonosítja a nem olvasható példány rögzített méretű tagját, akkor a E.I kiértékelése és besorolása System.Span<S>típusú értékként történik , ahol S a Ielemtípusa.
Az űrlap E.Itaghozzáférésében, ha az I tagkeresése nem olvashatóan statikus rögzített méretű tagot azonosít, akkor a E.I kiértékelése és besorolása System.Span<S>típusú értékként történik , ahol az S a Ielemtípusa .
Olvashatóan rögzített méretű pufferek
Ha egy field_declaration tartalmaz egy readonly módosító elemet, a fixed_size_buffer_declarator által bevezetett tag egy csak olvasható, fix méretű puffer.
Az olvashatóan rögzített méretű puffer elemeihez való közvetlen hozzárendelések csak azonos típusú példánykonstruktorban, init tagban vagy statikus konstruktorban fordulhatnak elő.
Pontosabban az olvashatóan rögzített méretű puffer egy eleméhez való közvetlen hozzárendelések csak a következő környezetekben engedélyezettek:
- Egy példánytag esetében a tagdeklarációt tartalmazó példánykonstruktorokban vagy init tagban; egy statikus tag esetében a tagdeklarációt tartalmazó típus statikus konstruktorában. Ezek az egyetlen olyan környezetek is, amelyekben érvényes az olvashatóan rögzített méretű puffer egy elemének átadása
outvagyrefparaméterként.
Fordítási időhiba, ha egy olvashatóan rögzített méretű puffer eleméhez próbál hozzá rendelni, vagy out vagy ref paraméterként átadni azt bármely más környezetben.
Ezt az alábbiakkal érheti el.
Egy olvashatóan rögzített méretű puffer taghozzáférése a következőképpen lesz kiértékelve és besorolva:
- Az űrlap
E.Itaghozzáférése esetén, haEszerkezet típusú, ésEértékként van besorolva, akkorE.Icsak elemhozzáférésiprimary_no_array_creation_expression használhatóSystem.Indextípusú indexkel, vagy implicit módon int értékké konvertálható típusként. Az elemhozzáférés eredménye egy rögzített méretű tag eleme a megadott helyen, értékként besorolva. - Ha a hozzáférés olyan környezetben történik, amelyben engedélyezett az olvashatóan rögzített méretű puffer egy elemének közvetlen hozzárendelése, a kifejezés eredménye
System.Span<S>típusú értékként lesz besorolva, ahol S a rögzített méretű puffer elemtípusa. Az érték a tagok elemeinek eléréséhez használható. - Ellenkező esetben a kifejezés
System.ReadOnlySpan<S>típusú értékként van besorolva, ahol az S a rögzített méretű puffer elemtípusa. Az érték a tagok elemeinek eléréséhez használható.
Határozott hozzárendelés-ellenőrzés
A rögzített méretű pufferek nem tartoznak meghatározott hozzárendelés-ellenőrzés alá, és a rögzített méretű puffertagokat a rendszer figyelmen kívül hagyja a szerkezettípus változóinak határozott hozzárendelés-ellenőrzése céljából.
Ha a rögzített méretű puffertag statikus, vagy a rögzített méretű puffertag strukturálási változóját tartalmazó legkülső változó statikus változó, egy osztálypéldány példányváltozója vagy tömbelem, a rögzített méretű puffer elemei automatikusan inicializálódnak az alapértelmezett értékükre. Minden más esetben a rögzített méretű puffer kezdeti tartalma nincs meghatározva.
Metaadatok
Metaadatok kibocsátása és kódgenerálás
A metaadat-kódolás során a fordító a nemrég hozzáadott System.Runtime.CompilerServices.InlineArrayAttribute-ra támaszkodik.
Rögzített méretű pufferek, például a következő pszeudokód:
// Not valid C#
public partial class C
{
public int buffer1[10];
public readonly int buffer2[10];
}
speciálisan díszített szerkezettípus mezőiként lesznek kibocsátva.
Az egyenértékű C#-kód a következő lesz:
public partial class C
{
public Buffer10<int> buffer1;
public readonly Buffer10<int> buffer2;
}
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer10<T>
{
private T _element0;
[UnscopedRef]
public System.Span<T> AsSpan()
{
return System.Runtime.InteropServices.MemoryMarshal.CreateSpan(ref _element0, 10);
}
[UnscopedRef]
public readonly System.ReadOnlySpan<T> AsReadOnlySpan()
{
return System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpan(
ref System.Runtime.CompilerServices.Unsafe.AsRef(in _element0), 10);
}
}
A típus és tagjai tényleges elnevezési konvenciói még eldöntésre várnak. A keretrendszer valószínűleg tartalmaz egy előre definiált "puffer" típusokat, amelyek korlátozott számú pufferméretet fednek le. Ha egy előre definiált típus nem létezik, a fordító szintetizálja azt a készülő modulban. A létrehozott típusok neve "beszédes" lesz, hogy támogassa a más nyelvekből származó felhasználást.
Egy hozzáféréshez létrehozott kód, például:
public partial class C
{
void M1(int val)
{
buffer1[1] = val;
}
int M2()
{
return buffer2[1];
}
}
egyenértékű lesz:
public partial class C
{
void M1(int val)
{
buffer.AsSpan()[1] = val;
}
int M2()
{
return buffer2.AsReadOnlySpan()[1];
}
}
Metaadatok importálása
Amikor a fordító importál egy T típusú meződeklarációt, és a következő feltételek teljesülnek:
-
T a
InlineArrayattribútummal díszített szerkezettípus, és - A T belül deklarált első példánymező Ftípusú, és
- Van egy
public System.Span<F> AsSpan()a T, és - A
public readonly System.ReadOnlySpan<T> AsReadOnlySpan()vagypublic System.ReadOnlySpan<T> AsReadOnlySpan()a Tbelül van.
A mező C# rögzített méretű pufferként lesz kezelve, Felemtípussal. Ellenkező esetben a mező normál típusú mezőként lesz kezelve, T.
Metódus vagy tulajdonságcsoport, amely a nyelvben alkalmazott megközelítésként szolgál
Az egyik gondolat, hogy ezeket a tagokat inkább metóduscsoportokként kezeljük, mivel ezek nem automatikusan önmagukban és önmagukban is értéknek minősülnek, hanem szükség esetén egybe is alakíthatók. Ez így működik:
- A biztonságos rögzített méretű pufferhozzáférések saját besorolással rendelkeznek (például metóduscsoportokhoz és lambdákhoz hasonlóan)
- Közvetlenül indexelhetők nyelvi műveletként (nem span típusokkal), hogy létrehozzanak egy változót (ami írásvédett, ha a puffer egy írásvédett környezetben található, ugyanúgy, mint egy struktúra mezői esetében).
- Implicit konverziójuk van a kifejezésből a
Span<T>és aReadOnlySpan<T>típusokra, de az előbbi használata hiba, ha ezek írásvédett környezetben vannak. - Természetes típusuk a
ReadOnlySpan<T>, így ez az, amihez hozzájárulnak, ha részt vesznek a típuskövetkeztetésben (pl. var, best-common-type vagy generic)
C/C++ rögzített méretű pufferek
A C/C++-nak eltérő fogalma van a rögzített méretű pufferekről. Létezik például a "nulla hosszúságú rögzített méretű pufferek" fogalma, amelyet gyakran használnak annak jelzésére, hogy az adatok "változó hosszúságúak". Ennek a javaslatnak nem az a célja, hogy képes legyen együttműködni ezzel.
LDM-értekezletek
- https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-04-03.md#fixed-size-buffers
- https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-04-10.md#fixed-size-buffers
- https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-05-01.md#fixed-size-buffers
- https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-05-03.md#inline-arrays
- https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-05-17.md#inline-arrays
- https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-06-17.md#inline-arrays-as-record-structs
C# feature specifications