Aracılığıyla paylaş


Satır içi Diziler

Uyarı

Bu makale bir özellik belirtimidir. Belirtim, özelliğin tasarım belgesi olarak görev alır. Önerilen belirtim değişikliklerini ve özelliğin tasarımı ve geliştirilmesi sırasında gereken bilgileri içerir. Bu makaleler, önerilen belirtim değişiklikleri son haline getirilene ve geçerli ECMA belirtimine dahil edilene kadar yayımlanır.

Özellik belirtimi ile tamamlanan uygulama arasında bazı tutarsızlıklar olabilir. Bu farklılıklar ilgili dil tasarım toplantısı (LDM) notlarında yakalanır.

Özellik belirtimlerini C# dil standardına benimseme işlemi hakkında daha fazla bilgi edinmek içinbelirtimleri makalesinde bulabilirsiniz.

Şampiyonluk sorunu: https://github.com/dotnet/csharplang/issues/7431

Özet

InlineArrayAttribute özelliğini kullanarak yapı türlerini kullanmak için genel amaçlı ve güvenli bir mekanizma sağlayın. C# sınıfları, yapıları ve arabirimleri içinde satır içi dizileri bildirmek için genel amaçlı ve güvenli bir mekanizma sağlayın.

Not: Bu belirtimin önceki sürümleri, Span güvenliği özellik belirtiminde kullanıma sunulan "ref-safe-to-escape" ve "safe-to-escape" terimlerini kullanıyordu. ECMA standart komitesi adlarını sırasıyla "ref-safe-context" ve "safe-context"olarak değiştirdi. Güvenli bağlamın değerleri tutarlı bir şekilde "declaration-block", "function-member" ve "caller-context" kullanacak şekilde geliştirilmiştir. Bölümcükler, bu terimler için farklı ifadeler kullanmış ve ayrıca "çağıran bağlam" için "dönüş için güvenli" ifadesini eş anlamlı olarak kullanmıştı. Bu belirtim, C# 7.3 standardındaki terimleri kullanacak şekilde güncelleştirildi.

Motivasyon

Bu teklif, birçok sınırlamasını ele almak için planlarını içerir https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/unsafe-code.md#238-fixed-size-buffers. Özellikle aşağıdakilere izin vermeyi amaçlar:

  • InlineArrayAttribute özelliğini kullanarak yapı türlerinin öğelerine erişme;
  • , veya structiçindeki yönetilen ve yönetilmeyen türler classinterfaceiçin satır içi dizilerin bildirimi.

Ve bunlar için dil güvenliği doğrulaması sağlayın.

Ayrıntılı Tasarım

Yakın zamanda çalışma zamanı InlineArrayAttribute özelliği eklendi. Kısacası, bir kullanıcı aşağıdaki gibi bir yapı türü bildirebilir:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
    private object _element0;
}

Çalışma zamanı, tür için Buffer özel bir tür düzeni sağlar:

  • Türün boyutu 10 'a sığacak şekilde genişletilir (sayı InlineArray özniteliğinden gelir) öğelerin türü object (tür, _element0 bu örnekte yapıdaki tek örnek alanının türünden gelir).
  • İlk öğe örnek alanıyla ve yapının başlangıcıyla hizalanır
  • Öğeler, bir dizinin öğeleriymiş gibi sırayla belleğe yerleştirilmiştir.

Çalışma zamanı, yapıdaki tüm öğeler için düzenli GC izlemesi sağlar.

Bu teklif, bunun gibi türlere "satır içi dizi türleri" olarak başvurur.

Satır içi dizi türünün öğelerine işaretçiler aracılığıyla veya System.Runtime.InteropServices.MemoryMarshal.CreateSpan/ API'leri tarafından döndürülen span örnekleri aracılığıyla erişilebilir. Ancak, ne işaretçi yaklaşımı ne de API'ler kutudan çıkış türü ve sınırları sağlamaz.

Dil, gömülü dizi türlerinin öğelerine erişmek için tür ve referans açısından güvenli bir yol sağlayacaktır. Erişim, kapsam bazlı olacaktır. Bu, desteği, tür bağımsız değişkeni olarak kullanılabilecek öğe türleriyle satır içi dizi türleriyle sınırlar. Örneğin, bir işaretçi türü bir öğe türü olarak kullanılamaz. Diğer örnekler, span türleridir.

Satır içi dizi türü için span türlerinin örneklerini alma

Satır içi dizi türündeki ilk öğenin türün başına hizalanacağına dair bir garanti olduğundan (boşluk yok), derleyici bir Span değer almak için aşağıdaki kodu kullanır:

MemoryMarshal.CreateSpan(ref Unsafe.As<TBuffer, TElement>(ref buffer), size);

Ve bir ReadOnlySpan değer almak için aşağıdaki kod:

MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<TBuffer, TElement>(ref Unsafe.AsRef(in buffer)), size);

Kullanım sitelerinde IL boyutunu küçültmek için derleyicisi, özel uygulama ayrıntı türüne iki genel yeniden kullanılabilir yardımcı ekleyebilmeli ve bunları aynı programdaki tüm kullanım sitelerinde kullanabilmelidir.

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);
}

Öğe erişimi

Öğe erişimi satır içi dizi öğesi erişimini destekleyecek şekilde genişletilecektir.

element_access, bir primary_no_array_creation_expressionile başlar, ardından "[" belirteci gelir, sonrasında bir argument_listve "]" belirteci ile sona erer. argument_list, bir veya daha fazla bağımsız değişkenden oluşur ve bunlar virgülle ayrılır.

element_access
    : primary_no_array_creation_expression '[' argument_list ']'
    ;

argument_list, bir element_access'in ref veya out bağımsız değişkenlerini içermesine izin verilmez.

Aşağıdakilerden en az biri barındırılıyorsa element_access dinamik olarak bağlıdır (§11.3.3):

  • primary_no_array_creation_expressiondynamicderleme zamanı türüne sahiptir.
  • argument_list en az bir ifadesi derleme zamanı türüne dynamic sahiptir ve primary_no_array_creation_expression bir dizi türü yoktur ve primary_no_array_creation_expression satır içi dizi türü yoktur veya bağımsız değişken listesinde birden fazla öğe vardır.

Bu durumda, derleyici element_accessdynamictüründe bir değer olarak sınıflandırır. Aşağıdaki kurallar element_access anlamını belirlemek için, derleme zamanı türü olan primary_no_array_creation_expression ve dynamic ifadelerinin derleme zamanı türü yerine çalışma zamanı türü kullanılarak çalışma zamanında uygulanır. primary_no_array_creation_expression derleme zamanı türü dynamicyoksa, öğe erişimi §11.6.5'te açıklandığı gibi sınırlı bir derleme zamanı denetiminden geçer.

element_accessprimary_no_array_creation_expression bir array_typedeğeriyse, element_access bir dizi erişimidir (§12.8.12.2). bir element_access primary_no_array_creation_expression satır içi dizi türünün değişkeni veya değeriyse ve argument_list tek bir bağımsız değişkenden oluşuyorsa, element_access satır içi dizi öğesi erişimidir. Aksi takdirde, primary_no_array_creation_expression bir veya daha fazla dizin oluşturucu üyesi olan bir sınıf, yapı veya arabirim türünün değişkeni veya değeri olmalıdır; bu durumda element_access bir dizin oluşturucu erişimidir (§12.8.12.3).

Satır içi dizi öğesi erişimi

Satır içi dizi öğesi erişimi için, element_access primary_no_array_creation_expression bir satır içi dizi türünün değişkeni veya değeri olmalıdır. Ayrıca, satır içi dizi öğesi erişiminin argument_list adlandırılmış bağımsız değişkenler içermesine izin verilmez. argument_list tek bir ifade içermesi ve ifadenin

  • veya türünde int
  • örtük olarak dönüştürülebilir veya int
  • örtük olarak dönüştürülebilir veya System.Index
  • örtük olarak dönüştürülebilir.System.Range
İfade türü int olduğunda

primary_no_array_creation_expression yazılabilir bir değişkense, satır içi dizi öğesi erişimini değerlendirmenin sonucu, primary_no_array_creation_expression'de yöntemi tarafından System.Span<T> InlineArrayAsSpan döndürülen bir örneğinde System.Span<T> bu tamsayı değeriyle çağırmaya public ref T this[int index] { get; } eşdeğer yazılabilir bir değişkendir. Ref-safety analizi amacıyla, erişimin ref-safe-context/güvenli bağlamı, imzalı static ref T GetItem(ref InlineArrayType array)bir yöntemin çağrılması için aynı değere eşdeğerdir. Sonuçta elde edilen değişken, yalnızca primary_no_array_creation_expression taşınabilirse taşınabilir olarak kabul edilir.

primary_no_array_creation_expression salt okunur bir değişkense, satır içi dizi öğesi erişimini değerlendirmenin sonucu, primary_no_array_creation_expression üzerinde yöntemi tarafından System.ReadOnlySpan<T> InlineArrayAsReadOnlySpan döndürülen bir örneğinde System.ReadOnlySpan<T> bu tamsayı değeriyle çağırmaya public ref readonly T this[int index] { get; } eşdeğer bir salt okunur değişkendir. Ref-safety analizi amacıyla, erişimin ref-safe-context/güvenli bağlamı, imzalı static ref readonly T GetItem(in InlineArrayType array)bir yöntemin çağrılması için aynı değere eşdeğerdir. Sonuçta elde edilen değişken, yalnızca primary_no_array_creation_expression taşınabilirse taşınabilir olarak kabul edilir.

primary_no_array_creation_expression bir değerse, satır içi dizi öğesi erişimini değerlendirmenin sonucu, primary_no_array_creation_expression'da yöntemi tarafından System.ReadOnlySpan<T> InlineArrayAsReadOnlySpan döndürülen bir örneğinde System.ReadOnlySpan<T> bu tamsayı değeriyle çağırmaya public ref readonly T this[int index] { get; } eşdeğer bir değerdir. Ref-safety analizi amacıyla, erişimin ref-safe-context/güvenli bağlamı, imzalı static T GetItem(InlineArrayType array)bir yöntemin çağrılması için aynı değere eşdeğerdir.

Örneğin:

[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
}

Bildirilen satır içi dizi sınırlarının dışında bir sabit ifadeyle satır içi diziye dizin oluşturmak bir derleme zamanı hatasıdır.

İfade örtük olarak dönüştürülebilir olduğunda int

İfade int'e dönüştürülür ve ardından öğe erişimi, İfade türü int olduğunda bölümünde açıklandığı gibi yorumlanır.

İfade örtük olarak dönüştürülebilir olduğunda System.Index

İfadeSystem.Index, derleme zamanında koleksiyonun uzunluğunun bilindiği ve primary_no_array_creation_expression satır içi dizi türündeki https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/ranges.md#implicit-index-supportöğe miktarına eşit olduğu varsayılarak, içinde açıklandığı gibi int tabanlı dizin değerine dönüştürülür. Ardından öğe erişimi, İfade türü int olduğunda bölümünde açıklandığı gibi yorumlanır.

İfade örtük olarak dönüştürülebilir olduğunda System.Range

primary_no_array_creation_expression yazılabilir bir değişkense, satır içi dizi öğesi erişimini değerlendirmenin sonucu, primary_no_array_creation_expression'de yöntemi tarafından System.Span<T> InlineArrayAsSpan döndürülen bir örneği System.Span<T> çağırmaya public Span<T> Slice (int start, int length) eşdeğer bir değerdir. Ref-safety analizi amacıyla, erişimin ref-safe-context/güvenli bağlamı, imzalı static System.Span<T> GetSlice(ref InlineArrayType array)bir yöntemin çağrılması için aynı değere eşdeğerdir.

primary_no_array_creation_expression salt okunur bir değişkense, satır içi dizi öğesi erişimini değerlendirmenin sonucu, primary_no_array_creation_expression'de yöntemi tarafından System.ReadOnlySpan<T> InlineArrayAsReadOnlySpan döndürülen bir örneği System.ReadOnlySpan<T> çağırmaya public ReadOnlySpan<T> Slice (int start, int length) eşdeğer bir değerdir. Ref-safety analizi amacıyla, erişimin ref-safe-context/güvenli bağlamı, imzalı static System.ReadOnlySpan<T> GetSlice(in InlineArrayType array)bir yöntemin çağrılması için aynı değere eşdeğerdir.

primary_no_array_creation_expression bir değerse bir hata bildirilir.

Yöntem çağrısının Slice bağımsız değişkenleri, derleme zamanında koleksiyonun uzunluğunun bilindiği ve primary_no_array_creation_expression satır içi dizi türündeki System.Rangeöğe miktarına eşit olduğu varsayılarak, dizinine dönüştürülen https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/ranges.md#implicit-range-supportifadeden hesaplanır.

Derleme zamanında Slice 0 olarak biliniyorsa ve start satır içi dizi türündeki öğe miktarına daha az veya eşitse derleyici çağrıyı atlayabilirlength. Derleyici ayrıca, derleme zamanında dilimlemenin satır içi dizi sınırları dışına çıktığı biliniyorsa bir hata bildirebilir.

Örneğin:

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
}

Dönüşümler

İfadeden yeni bir dönüştürme, satır içi dizi dönüştürmesi eklenir. Satır içi dizi dönüştürme, standart bir dönüştürmedir.

Satır içi dizi türünün ifadesinden aşağıdaki türlere örtük bir dönüştürme vardır:

  • System.Span<T>
  • System.ReadOnlySpan<T>

Ancak salt okunur bir değişkenin türüne System.Span<T> dönüştürülmesi veya herhangi bir türe dönüştürülmesi bir hatadır.

Örneğin:

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
}

Ref-safety analizi amacıyla, dönüştürmenin güvenli bağlamı, veya imzasıyla static System.Span<T> Convert(ref InlineArrayType array)bir yöntemin çağrılması için güvenli bağlamastatic System.ReadOnlySpan<T> Convert(in InlineArrayType array).

Liste şablonları

Satır içi dizi türleri örnekleri için liste desenleri desteklenmez.

Kesin atama denetimi

Normal kesin atama kuralları, satır içi dizi türüne sahip değişkenler için geçerlidir.

Koleksiyon değişmez değerleri

Satır içi dizi türünün örneği, spread_element geçerli bir ifadedir.

Aşağıdaki özellik C# 12'de gösterilmedi. Açık bir teklif olmaya devam ediyor. Bu örnekteki kod CS9174 oluşturur:

Satır içi dizi türü, koleksiyon ifadesi için geçerli bir oluşturulduğunda koleksiyon hedef türüdür. Örneğin:

Buffer10<int> b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // initializes user-defined inline array

Koleksiyon değişmez değerinin uzunluğu, hedef satır içi dizi türünün uzunluğuyla eşleşmelidir. Derleme zamanında değişmez değerin uzunluğu biliniyorsa ve hedef uzunlukla eşleşmiyorsa bir hata bildirilir. Aksi takdirde, uyumsuzlukla karşılaşıldığında çalışma zamanında bir özel durum oluşturulur. Tam özel durum türü TBD'dir. Bazı adaylar şunlardır: System.NotSupportedException, System.InvalidOperationException.

InlineArrayAttribute uygulamalarını doğrulama

Derleyici, InlineArrayAttribute uygulamalarının aşağıdaki yönlerini doğrular:

  • Hedef türü kayıt dışı bir yapıdır
  • Hedef türün yalnızca bir alanı var
  • Belirtilen uzunluk > 0
  • Hedef yapıda belirtilen açık bir düzen yok

Nesne başlatıcıdaki Satır içi Dizi öğeleri

Varsayılan olarak, öğe başlatma formun'[' argument_list ']' initializer_target aracılığıyla desteklenmez (bkz 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;
}

Ancak, satır içi dizi türü açıkça uygun dizin oluşturucu tanımlarsa, nesne başlatıcı bunu kullanır:

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;
    }
}

foreach deyimi

foreach deyimi , satır içi dizi türünün foreach deyiminde koleksiyon olarak kullanımına izin verecek şekilde ayarlanır.

Örneğin:

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;

eşdeğerdir:

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);
}

Span türlerinin çeviriye katılımı nedeniyle yöntemlerde foreach kısıtlanmış olarak başlasa bile satır içi dizileri destekleyeceğizasync.

Açık tasarım soruları

Alternatifler

Satır içi dizi türü söz dizimi

konumundaki dil bilgisi https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/types.md#821-general aşağıdaki gibi ayarlanır:

array_type
    : non_array_type rank_specifier+
    ;

rank_specifier
    : '[' ','* ']'
+   | '[' constant_expression ']' 
    ;

constant_expression türü, türüne intörtük olarak dönüştürülebilir olmalıdır ve değer sıfır olmayan pozitif bir tamsayı olmalıdır.

Bölümün https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/arrays.md#1721-general ilgili bölümü aşağıdaki gibi ayarlanır.

Dizi türleri için dil bilgisi üretimleri içinde https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/types.md#821-generalsağlanır.

Dizi türü bir non_array_type ve ardından bir veya daha fazla rank_specifierolarak yazılır.

non_array_type, kendisi array_type olmayan herhangi bir türdür.

Dizi türünün sırası, array_type en soldaki rank_specifiertarafından verilir: rank_specifier, dizinin bir derecesine ve rank_specifier "," belirteç sayısına sahip bir dizi olduğunu gösterir.

Bir dizi türünün öğe türü, en soldaki rank_specifier silinmesinden kaynaklanan türdür:

  • Formun T[ constant_expression ] dizi türü, uzunluğu constant_expression ve dizi olmayan öğe türüyle belirtilen anonim bir satır içi dizi türüdürT.
  • Formun T[ constant_expression ][R₁]...[Rₓ] dizi türü, uzunluğu constant_expression ve öğe türü ile belirtilen anonim bir satır içi dizi türüdürT[R₁]...[Rₓ].
  • Formun T[R] dizi türü (burada R bir constant_expression değildir), rank R ve dizi olmayan öğe türüne sahip normal bir dizi türüdür T.
  • Formun T[R][R₁]...[Rₓ] dizi türü (burada R bir constant_expression değildir), rank R ve öğe türüne sahip normal bir dizi türüdür T[R₁]...[Rₓ].

Sonuç olarak, dizi olmayan son öğe türünden önce rank_specifiersoldan sağa okunur.

Örnek: içindeki int[][,,][,] türü, iki boyutlu dizilerinden oluşan üç boyutlu dizilerden oluşan tek boyutlu bir dizidir int. son örnek

Çalışma zamanında, normal bir dizi türünün değeri veya bu dizi türünün bir örneğine başvuru olabilir null .

Not: kurallarına uyularak https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/arrays.md#176-array-covariance, değer aynı zamanda bir kovaryant dizi türüne başvuru da olabilir. dipnot

Anonim satır içi dizi türü, iç erişilebilirlikle birleştirilmiş bir derleyici satır içi dizi türüdür. Öğe türü, tür bağımsız değişkeni olarak kullanılabilecek bir tür olmalıdır. Açıkça bildirilen satır içi dizi türünden farklı olarak, anonim satır içi dizi türüne ada göre başvurulamaz, yalnızca array_type söz diziminde başvurulabilir. Aynı program bağlamında, aynı öğe türünde ve aynı uzunlukta satır içi dizi türlerini belirten iki array_typeaynı anonim satır içi dizi türüne başvurur.

İç erişilebilirliğe ek olarak, derleyici, imzadaki anonim satır içi dizi türü başvurusuna uygulanan gerekli bir özel değiştirici (tam tür TBD) kullanarak derleme sınırları boyunca anonim satır içi dizi türlerini kullanan API'lerin tüketimini engeller.

Dizi oluşturma ifadeleri

Dizi oluşturma ifadeleri

array_creation_expression
    : 'new' non_array_type '[' expression_list ']' rank_specifier*
      array_initializer?
    | 'new' array_type array_initializer
    | 'new' rank_specifier array_initializer
    ;

Geçerli dil bilgisi göz önünde bulundurulduğunda, expression_list yerine bir constant_expression kullanılması, belirtilen uzunlukta normal bir tek boyutlu dizi türü ayırma anlamına zaten sahiptir. Bu nedenle, array_creation_expression normal bir dizi ayırmasını temsil etmeye devam eder.

Ancak, rank_specifier yeni biçimi ayrılan dizinin öğe türüne anonim bir satır içi dizi türü eklemek için kullanılabilir.

Örneğin, aşağıdaki ifadeler, öğe türü int ve uzunluğu 5 olan anonim satır içi dizi türündeki bir öğe türüyle 2 uzunluğunda normal bir dizi oluşturur:

new int[2][5];
new int[][5] {default, default};
new [] {default(int[5]), default(int[5])};

Dizi başlatıcıları

Dizi başlatıcıları C# 12'de uygulanmadı. Bu bölüm etkin bir teklif olmaya devam eder.

Dizi başlatıcıları bölümü, satır içi dizi türlerini başlatmak için array_initializer kullanımına izin verecek şekilde ayarlanır (dil bilgisi için değişiklik gerekmez).

array_initializer
    : '{' variable_initializer_list? '}'
    | '{' variable_initializer_list ',' '}'
    ;

variable_initializer_list
    : variable_initializer (',' variable_initializer)*
    ;
    
variable_initializer
    : expression
    | array_initializer
    ;

Satır içi dizinin uzunluğu hedef tür tarafından açıkça sağlanmalıdır.

Örneğin:

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

Ayrıntılı Tasarım (Seçenek 2)

Bu teklifin amacı için bir "sabit boyutlu arabellek" teriminin, adresinde https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/unsafe-code.md#238-fixed-size-buffersaçıklanan bir arabellek yerine önerilen "güvenli sabit boyutlu arabellek" özelliğini ifade ettiğini unutmayın.

Bu tasarımda, sabit boyutlu arabellek türleri dile göre genel özel işlem yapmaz. Sabit boyutlu arabellekleri ve bu üyelerin tüketilmesiyle ilgili yeni kuralları temsil eden üyeleri bildirmek için özel bir söz dizimi vardır. Bunlar dil açısından alanlar değildir.

içindeki variable_declaratorhttps://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#155-fieldsdil bilgisi, arabelleğin boyutunun belirtilmesine izin verecek şekilde genişletilecektir:

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 ']'
    ;    

fixed_size_buffer_declarator, belirli bir öğe türünün sabit boyutlu arabelleğine sahiptir.

Arabellek öğesi türü, içinde belirtilen türdür.field_declaration Sabit boyutlu arabellek bildirimcisi yeni bir üyeyi tanıtır ve üyeyi adlandıran bir tanımlayıcıdan, ardından ve [ belirteçleri içine alınmış ] sabit bir ifadeden oluşur. Sabit ifade, bu sabit boyutlu arabellek bildirimcisi tarafından tanıtılan üyedeki öğelerin sayısını belirtir. Sabit ifadenin türü, türüne intörtük olarak dönüştürülebilir olmalı ve değer sıfır olmayan pozitif bir tamsayı olmalıdır.

Sabit boyutlu bir arabelleğin öğeleri, bir dizinin öğeleriymiş gibi sırayla belleğe yerleştirilmelidir.

Arabirimde fixed_size_buffer_declarator olan bir field_declaration değiştiricisi static olmalıdır.

Duruma bağlı olarak (ayrıntılar aşağıda belirtilmiştir), sabit boyutlu arabellek üyesine erişim, sabit boyutlu arabelleğin öğe türü olan veya System.ReadOnlySpan<S>değerlerinin System.Span<S> değeri (hiçbir zaman değişken değildir) olarak sınıflandırılır. Her iki tür de uygun "readonly-ness" ile belirli bir öğeye başvuru döndüren dizin oluşturucular sağlar ve bu da dil kuralları buna izin vermediğinde öğelere doğrudan atamayı engeller.

Bu, sabit boyutlu arabellek öğesi türü olarak kullanılabilecek tür kümesini tür bağımsız değişkenleri olarak kullanılabilecek türlerle sınırlar. Örneğin, bir işaretçi türü bir öğe türü olarak kullanılamaz.

Sonuçta elde edilen span örneği, sabit boyutlu arabellekte bildirilen boyuta eşit bir uzunluğa sahip olur. Belirtilen sabit boyutlu arabellek sınırlarının dışında bir sabit ifadeyle span'a dizin oluşturmak bir derleme zamanı hatasıdır.

Değerin güvenli bağlamı , aynı yedekleme verilerine alan olarak erişildiğinde olduğu gibi kapsayıcının güvenli bağlamı ile eşit olacaktır.

İfadelerdeki sabit boyutlu arabellekler

Sabit boyutlu arabellek üyesinin üye araması, bir alanın üye araması gibi devam eder.

bir simple_name veya member_access kullanılarak bir ifadede sabit boyutlu arabelleğe başvurulabilir.

Örnek sabit boyutlu arabellek üyesine basit bir ad olarak başvurulduğunda, etki, sabit boyutlu arabellek üyesi olan this.I formunun Iüye erişimiyle aynıdır. Statik bir sabit boyutlu arabellek üyesine basit bir ad olarak başvurulduğunda, etki, sabit boyutlu arabellek üyesi ve E.I bildirim türü olan I formunun Eüye erişimiyle aynıdır.

Salt okunur olmayan sabit boyutlu arabellekler

formunun E.Iüye erişiminde, bir yapı türündeyse E ve bu yapı türünde bir üye araması I , salt okunur olmayan bir örnek sabit boyutlu üye tanımlar, ardından E.I değerlendirilir ve aşağıdaki gibi sınıflandırılır:

  • E Değer olarak sınıflandırılırsa, E.I yalnızca türü dizini olan bir öğe erişimininveya örtük olarak int'e dönüştürülebilen bir türün primary_no_array_creation_expressionSystem.Indexkullanılabilir. Öğe erişiminin sonucu, belirtilen konumdaki sabit boyutlu bir üyenin öğesidir ve değer olarak sınıflandırılır.
  • Aksi takdirde, salt okunur bir değişken olarak sınıflandırılırsa E ve ifadenin sonucu türünde System.ReadOnlySpan<S>bir değer olarak sınıflandırılırsa, burada S öğesi türüdür I. değeri üyenin öğelerine erişmek için kullanılabilir.
  • Aksi takdirde, E yazılabilir değişken olarak sınıflandırılır ve ifadenin sonucu türündeki System.Span<S>bir değer olarak sınıflandırılır; burada S, öğesinin Iöğe türüdür. değeri üyenin öğelerine erişmek için kullanılabilir.

formunun E.Iüye erişiminde, bir sınıf türündeyse E ve bu sınıf türünde üye araması I , salt okunur olmayan bir örnek sabit boyutlu üye tanımlar, ardından E.I değerlendirilir ve türündeki System.Span<S>bir değer olarak sınıflandırılır; burada S öğesi türüdür I.

formunun E.Iüye erişiminde, öğesinin I üye araması salt okunur olmayan statik sabit boyutlu bir üye tanımlarsa, E.I değerlendirilir ve türünde System.Span<S>bir değer olarak sınıflandırılır; burada S öğesi türüdür I.

Salt okunur sabit boyutlu arabellekler

bir field_declaration değiştirici readonly içerdiğinde, fixed_size_buffer_declarator tarafından tanıtılan üye salt okunur bir sabit boyutlu arabellektir. Salt okunur sabit boyutlu arabelleğin öğelerine doğrudan atamalar yalnızca aynı türdeki bir örnek oluşturucusunda, init üyesinde veya statik oluşturucuda gerçekleşebilir. Özellikle, salt okunur sabit boyutlu arabellek öğesine doğrudan atamalara yalnızca aşağıdaki bağlamlarda izin verilir:

  • Bir örnek üyesi için, üye bildirimini içeren türün örnek oluşturucularında veya init üyesinde; statik bir üye için, üye bildirimini içeren türün statik oluşturucusunda. Bunlar aynı zamanda salt okunur sabit boyutlu arabellek öğesini veya out parametresi olarak ref geçirmek için geçerli olduğu tek bağlamlardır.

Salt okunur sabit boyutlu bir arabelleğin öğesine atamaya veya başka bir out bağlamda veya ref parametresi olarak geçirmeye çalışmak derleme zamanı hatasıdır. Bu, aşağıdakiler tarafından elde edilir.

Salt okunur sabit boyutlu arabellek için üye erişimi değerlendirilir ve aşağıdaki gibi sınıflandırılır:

  • biçimindeki E.Ibir üye erişiminde, bir yapı türündeyse E ve E değer olarak sınıflandırılırsa, E.I yalnızca türü dizini olan veya System.Index olarak kullanılabilir. Öğe erişiminin sonucu, belirtilen konumdaki sabit boyutlu bir üyenin öğesidir, bir değer olarak sınıflandırılır.
  • Erişim, salt okunur sabit boyutlu arabellek öğesine doğrudan atamalara izin verilen bir bağlamda gerçekleşirse, ifadenin sonucu türünde System.Span<S>bir değer olarak sınıflandırılır; burada S, sabit boyutlu arabelleğin öğe türüdür. değeri üyenin öğelerine erişmek için kullanılabilir.
  • Aksi takdirde, ifade türündeki System.ReadOnlySpan<S>bir değer olarak sınıflandırılır; burada S, sabit boyutlu arabelleğin öğe türüdür. değeri üyenin öğelerine erişmek için kullanılabilir.

Kesin atama denetimi

Sabit boyutlu arabellekler kesin atama denetimine tabi değildir ve yapı türü değişkenlerinin kesin atama denetimi amacıyla sabit boyutlu arabellek üyeleri yoksayılır.

Sabit boyutlu arabellek üyesi statik olduğunda veya sabit boyutlu arabellek üyesinin en dıştaki yapı değişkeni statik değişken, sınıf örneğinin örnek değişkeni veya dizi öğesi olduğunda, sabit boyutlu arabellek öğeleri otomatik olarak varsayılan değerlerine başlatılır. Diğer tüm durumlarda, sabit boyutlu bir arabelleğin ilk içeriği tanımlanmamıştır.

Meta veriler

Meta veri yayma ve kod oluşturma

Meta veri kodlama derleyicisi için yakın zamanda eklenen 'i kullanır System.Runtime.CompilerServices.InlineArrayAttribute.

Aşağıdaki sahte kod gibi sabit boyutlu arabellekler:

// Not valid C#
public partial class C
{
    public int buffer1[10];
    public readonly int buffer2[10];
}

özel olarak düzenlenmiş bir yapı türünün alanları olarak yayılacaktır.

Eşdeğer C# kodu şöyle olacaktır:

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);
    }
}

Türü ve üyeleri için gerçek adlandırma kuralları TBD'dir. Çerçeve büyük olasılıkla sınırlı bir arabellek boyutu kümesini kapsayan önceden tanımlanmış bir "arabellek" türleri kümesi içerir. Önceden tanımlanmış bir tür olmadığında, derleyici bunu derlenen modülde sentezler. Oluşturulan türlerin adları, diğer dillerden gelen tüketimi desteklemek için "söylenebilir" olacaktır.

Aşağıdaki gibi bir erişim için oluşturulan kod:

public partial class C
{
    void M1(int val)
    {
        buffer1[1] = val;
    }

    int M2()
    {
        return buffer2[1];
    }
}

eşdeğer olacaktır:

public partial class C
{
    void M1(int val)
    {
        buffer.AsSpan()[1] = val;
    }

    int M2()
    {
        return buffer2.AsReadOnlySpan()[1];
    }
}
Meta veri içeri aktarma

Derleyici T türünde bir alan bildirimini içeri aktardığında ve aşağıdaki koşulların tümü karşılandığında:

  • T , özniteliğiyle InlineArray süslenmiş bir yapı türüdür ve
  • T içinde bildirilen ilk örnek alanı F türüne ve
  • Tpublic System.Span<F> AsSpan()bir vardır ve
  • T içinde bir public readonly System.ReadOnlySpan<T> AsReadOnlySpan() veya public System.ReadOnlySpan<T> AsReadOnlySpan()vardır.

alanı, F öğe türüne sahip C# sabit boyutlu arabellek olarak değerlendirilir. Aksi takdirde, alan T türünde normal bir alan olarak değerlendirilir.

Dildeki yaklaşım gibi yöntem veya özellik grubu

Bu üyeleri yöntem gruplarına daha çok benzetmek, bu üyelerin kendilerinde ve içinde otomatik olarak bir değer olmadığından, ancak gerekirse tek bir değer haline getirilebileceği düşünülüyor. Şu şekilde çalışır:

  • Güvenli sabit boyutlu arabellek erişimlerinin kendi sınıflandırmaları vardır (örneğin yöntem grupları ve lambdalar gibi)
  • Bir değişken üretmek için doğrudan bir dil işlemi (span türleri aracılığıyla değil) olarak dizine eklenebilirler (arabellek salt okunur bağlamdaysa salt okunurdur, bir yapının alanlarıyla aynıdır)
  • ve ifadesinden Span<T>ReadOnlySpan<T>örtük dönüştürmeleri vardır, ancak salt okunur bir bağlamdaysa, öncekinin kullanımı bir hatadır
  • Doğal türleri olur ReadOnlySpan<T>, dolayısıyla tür çıkarımına katılırlarsa katkıda bulundukları şey budur (örneğin, var, en iyi ortak tür veya genel)

C/C++ sabit boyutlu arabellekler

C/C++ sabit boyutlu arabellekler farklı bir ifadeye sahiptir. Örneğin, genellikle verilerin "değişken uzunluk" olduğunu göstermek için kullanılan "sıfır uzunluklu sabit boyutlu arabellekler" diye bir not vardır. Bu teklifle birlikte çalışabilmek bu teklifin amacı değildir.

LDM toplantıları