Aracılığıyla paylaş


Koleksiyon ifadeleri

Not

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, dil tasarım toplantısı (LDM) hakkındakinotlarda yer alır.

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

Şampiyon sorunu: https://github.com/dotnet/csharplang/issues/8652

Özet

Koleksiyon ifadeleri, ortak koleksiyon değerleri oluşturmak için [e1, e2, e3, etc]yeni bir terse söz dizimi ekler. Bu değerlere diğer koleksiyonların dahil edilmesi, ..e gibi bir spread öğesi kullanılarak [e1, ..c2, e2, ..c2]mümkündür.

Dış BCL desteği gerekmeden çeşitli koleksiyon benzeri türler oluşturulabilir. Bu türler şunlardır:

Doğrudan türün kendisinde benimsenebilen yeni bir öznitelik ve API deseni aracılığıyla yukarıda ele alınmayan koleksiyon benzeri türler için daha fazla destek sağlanır.

Motivasyon

  • Koleksiyon benzeri değerler programlama, algoritmalar ve özellikle C#/.NET ekosisteminde büyük ölçüde mevcuttur. Neredeyse tüm programlar verileri depolamak ve diğer bileşenlerden veri göndermek veya almak için bu değerleri kullanır. Şu anda, neredeyse tüm C# programları bu tür değerlerin örneklerini oluşturmak için birçok farklı ve ne yazık ki ayrıntılı yaklaşımlar kullanmalıdır. Bazı yaklaşımların performans dezavantajları da vardır. Aşağıda bazı yaygın örnekler verilmiştir:

    • new Type[] değerlerinden önce new[] veya { ... } gerektiren diziler.
    • Spans, stackalloc ve diğer hantal yapıları kullanabilen.
    • Koleksiyon başlatıcıları, değerlerinden önce büyük olasılıkla ayrıntılı bir new List<T>çıkarımının eksik olduğu T gibi bir söz dizimi gerektirir ve başlangıç kapasitesini sağlamadan N .Add çağrılarını kullandıkları için belleğin birden çok kez yeniden yerleştirilmesine neden olabilir.
    • Değerleri başlatmak için ImmutableArray.Create(...) gibi bir söz dizimi gerektiren ve geçici bellek ayırmalarına ve veri kopyalamaya neden olabilen değişmez koleksiyonlar. Daha verimli inşaat formları (ImmutableArray.CreateBuildergibi) kullanımı zor ve yine de kaçınılmaz çöp üretir.
  • Çevresindeki ekosisteme baktığımızda, liste oluşturmanın daha kullanışlı ve kullanımı daha keyifli olduğu her yerde örnekler de buluyoruz. TypeScript, Dart, Swift, Elm, Python ve daha fazlası, bu amaç doğrultusunda yaygın kullanımla ve büyük bir etkiyle kısa bir söz dizimini tercih eder. Yüzeysel incelemeler, bu sabitlerin yerleşik olmasıyla bu ekosistemlerde hiçbir önemli sorunun ortaya çıkmasına neden olmadığını ortaya koymuştur.

  • C# ayrıca C# 11'de liste desenleri ekledi. Bu desen, temiz ve sezgisel bir söz dizimi kullanarak liste benzeri değerlerin eşleştirilmesine ve yapılandırılmasına olanak tanır. Ancak, diğer tüm desen yapılarından farklı olarak, bu eşleştirme/yapısızlaştırma söz diziminde karşılık gelen yapı söz dizimi eksiktir.

  • Her koleksiyon türünü oluşturmak için en iyi performansı elde etme zor olabilir. Basit çözümler genellikle hem CPU hem de bellek harcar. Değişmez değer biçimine sahip olmak, derleyici uygulamasının değişmez değeri optimize ederek, bir kullanıcının yapabileceği en iyi kadar iyi bir sonuç elde etmesini sağlayacak en yüksek esnekliği, ancak basit bir kod ile sunar. Çoğu zaman derleyici daha iyisini yapabilir ve belirtim, bunu sağlamak için uygulama stratejisine büyük ölçüde esneklik sağlamayı amaçlar.

C# için kapsayıcı bir çözüm gereklidir. Sahip oldukları koleksiyon benzeri türler ve değerler açısından müşterilerin büyük çoğunlunun ihtiyaçlarını karşılamalıdır. Ayrıca dilde doğal hissetmeli ve desen eşleştirmede yapılan çalışmayı yansıtmalıdır.

Bu, söz diziminin [e1, e2, e3, e-etc] ve [e1, ..c2, e2]desen eşdeğerlerine karşılık gelen [p1, p2, p3, p-etc] veya [p1, ..p2, p3]gibi olması gerektiği konusunda doğal bir sonuca yol açar.

Ayrıntılı tasarım

Aşağıdaki dil bilgisi üretimleri eklenir:

primary_no_array_creation_expression
  ...
+ | collection_expression
  ;

+ collection_expression
  : '[' ']'
  | '[' collection_element ( ',' collection_element )* ']'
  ;

+ collection_element
  : expression_element
  | spread_element
  ;

+ expression_element
  : expression
  ;

+ spread_element
  : '..' expression
  ;

Koleksiyon değişmez değerleri, hedef türü.

Spesifikasyon açıklamaları

  • Kısalık için, collection_expression aşağıdaki bölümlerde "literal" olarak adlandırılır.

  • expression_element örnekleri genellikle e1, e_nvb. olarak adlandırılır.

  • spread_element örnekleri genellikle ..s1, ..s_nvb. olarak adlandırılır.

  • span türüSpan<T> veya ReadOnlySpan<T>anlamına gelir.

  • Sabit değerler, herhangi bir sırada ve herhangi bir sayıda öğeyi ifade etmek için genellikle [e1, ..s1, e2, ..s2, etc] olarak gösterilir. Daha da önemlisi, bu form aşağıdakiler gibi tüm servis taleplerini temsil etmek için kullanılacaktır:

    • Boş sabit ifadeler []
    • İçerisinde expression_element içermeyen sabitler.
    • İçlerinde spread_element olmayan sabit değerler.
    • Rastgele sıralanabilen herhangi bir öğe türüne sahip sabit değerler.
  • ..s_n yineleme türü, bir üzerinde yinelenen ifade olarak s_n kullanılmış gibi belirlenen foreach_statement yineleme değişkeninin türüdür.

  • __name ile başlayan değişkenler, yalnızca bir kez değerlendirilecek şekilde bir konumda depolanan namedeğerlendirmesinin sonuçlarını göstermek için kullanılır. Örneğin __e1, e1değerlendirmesidir.

  • List<T>, IEnumerable<T>vb. System.Collections.Generic ad alanında ilgili türlere bakın.

  • Tanım, literalin mevcut C# yapılarına çevirisini tanımlar. Sorgu ifadesi çevirisine benzer şekilde, yalnızca çevirinin yasal kod oluşturduğu durumlarda değişmez değer yasaldır. Bu kuralın amacı, ima edilen diğer dil kurallarını yinelemek zorunda kalmaktan kaçınmaktır (örneğin, depolama konumlarına atandığında ifadelerin dönüştürülebilirliği hakkında).

  • Aşağıda belirtilen sabitleri tam olarak çevirmek için bir uygulama gerekmez. Aynı sonuç elde edilirse ve sonucun üretiminde gözlemlenebilir bir fark yoksa, çeviri yasaldır.

    • Örneğin, bir uygulama [1, 2, 3] gibi sabit değerleri doğrudan ham verileri tümleştirilmiş koda entegre eden bir new int[] { 1, 2, 3 } ifadesine çevirebilir ve böylelikle __index ihtiyacını veya her değeri atamak için gereken bir talimat dizisini ortadan kaldırabilir. Daha da önemlisi, bu, çevirinin herhangi bir adımının çalışma zamanında bir özel duruma neden olabilmesi halinde, program durumunun yine de çeviri tarafından belirtilen durumda kalacağı anlamına gelir.
  • 'Yığın ayırma' başvuruları, öbek değil yığında ayırmaya yönelik herhangi bir stratejiye atıfta bulunur. Önemli olan, bu stratejinin gerçek stackalloc mekanizması aracılığıyla olması anlamına gelmez veya gerektirmez. Örneğin, satır içi dizilerin kullanılması, kullanılabilir olduğunda yığın ayırmayı gerçekleştirmek için izin verilen ve istenen bir yaklaşımdır. C# 12'de satır içi dizilerin bir koleksiyon ifadesiyle başlatılamayacağını unutmayın. Bu açık bir teklif olmaya devam ediyor.

  • Koleksiyonların düzenli ve uyumlu olduğu varsayılır. Örneğin:

    • Bir koleksiyondaki Count değerinin numaralandırıldığında öğe sayısıyla aynı değeri üreteceği varsayılır.
    • System.Collections.Generic ad alanında tanımlanan bu belirtimde kullanılan türlerin yan etkisiz olduğu varsayılır. Bu nedenle, derleyici bu tür türlerin aracı değerler olarak kullanılabileceğini ancak aksi takdirde kullanıma sunulmayabileceği senaryoları iyileştirebilir.
    • Bir koleksiyondaki bazı uygun .AddRange(x) üyelerine yapılan bir çağrının, x üzerinden yineleyip her bir numaralandırılmış değeri .Addile koleksiyona tek tek ekleme işlemiyle aynı nihai değeri vereceği varsayılır.
    • İyi çalışmayan koleksiyonlara sahip koleksiyon sabitlerinin davranışı tanımlanmamıştır.

Dönüşüm

koleksiyon ifadesi dönüştürme koleksiyon ifadesinin bir türe dönüştürülmesini sağlar.

Bir koleksiyon ifadesinden aşağıdaki türlere örtük koleksiyon ifadesi dönüştürme var:

  • tek boyutlu dizi türüT[], bu durumda öğe türü, T olur
  • span türü:
    • System.Span<T>
    • System.ReadOnlySpan<T>
      Bu durumda öğe türüT
  • uygunoluşturma yöntemine sahip türü, bu durumda öğe türü bir uzantı yönteminden değil, örnek yönteminden veya numaralandırılabilir arabirimden belirlenen GetEnumerator türüdür
  • yapı veya sınıf türü uygulayan System.Collections.IEnumerable
    • türü, bağımsız değişken olmadan çağrılabilen uygun bir oluşturucuya sahiptir ve oluşturucuya koleksiyon ifadesinin bulunduğu yerden erişilebilir.

    • Koleksiyon ifadesinde herhangi bir öğe varsa, türüAdd bir örnek veya uzantı yöntemine sahiptir:

      • Yöntem, tek bir değer bağımsız değişkeniyle çağrılabilir.
      • Yöntem genelse, tür bağımsız değişkenleri koleksiyon ve bağımsız değişkenden türetilebilir.
      • yöntemine koleksiyon ifadesinin konumundan erişilebilir.

      Bu durumda öğe türü, türünün yineleme türü olur.

  • bir arabirim türü:
    • System.Collections.Generic.IEnumerable<T>
    • System.Collections.Generic.IReadOnlyCollection<T>
    • System.Collections.Generic.IReadOnlyList<T>
    • System.Collections.Generic.ICollection<T>
    • System.Collections.Generic.IList<T>
      Bu durumda öğe tipiT olur.

Koleksiyon ifadesindeki her öğesiT için, türün bir öğe türüEᵢ varsa örtük bir dönüştürme vardır.

  • Eᵢ birifade öğesiyse, Eᵢ'den T'e örtük bir dönüştürme vardır.
  • Eᵢ, ..Sᵢbir yayma öğesiyse, 'den Sᵢ'ye T yineleme türü için örtük bir dönüştürme vardır.

Koleksiyon ifadesinden çok boyutlu dizi türü'ye bir koleksiyon ifadesi dönüşümü yoktur.

Koleksiyon ifadesinden örtük olarak dönüştürülebilen türler, o koleksiyon ifadesi için geçerli hedef türlerdir.

koleksiyon ifadesindenaşağıdaki ek örtük dönüştürmeler vardır:

  • nullable değer türüneT?, burada koleksiyon ifadesinden bir değer türüne dönüşüm yapılmaktadırT. Dönüştürme, koleksiyon ifadesi dönüştürmesiniT'e sırasıyla 'dan T'ya örtük null yapılabilir dönüştürme T? takip eder.

  • T başvuru türüne bir oluşturma yöntemi, T ile ilişkilendirilmiş ve U'dan 'a bir U olan bir tür T döndüren. Dönüştürme, koleksiyon ifadesine dönüştürme'den U'ye, ardından 'den U'ya T şeklindedir.

  • Arabirim türüne I ile ilişkilendirilmiş bir tür döndüren bir V ve VI. Dönüştürme, koleksiyon ifadesi dönüştürmesi'nin ardından 'ten 'e bir örtük kutulama dönüştürmesi ile tamamlanır.

Yöntem oluşturma

oluşturma yöntemi, [CollectionBuilder(...)]koleksiyon türünde bir özniteliğiyle gösterilir. özniteliği, koleksiyon türünün bir örneğini oluşturmak için çağrılacak yöntemin oluşturucu türü ve yöntem adını belirtir.

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(
        AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface,
        Inherited = false,
        AllowMultiple = false)]
    public sealed class CollectionBuilderAttribute : System.Attribute
    {
        public CollectionBuilderAttribute(Type builderType, string methodName);
        public Type BuilderType { get; }
        public string MethodName { get; }
    }
}

Öznitelik, bir class, struct, ref structveya interfaceolarak uygulanabilir. Öznitelik bir temel class veya abstract class'e uygulanabilir, ancak devralınmamıştır.

oluşturucu türü genel olmayan bir class veya structolmalıdır.

İlk olarak, CMoluşturma yöntemleri kümesi belirlenir.
Aşağıdaki gereksinimleri karşılayan yöntemlerden oluşur:

  • Metot, [CollectionBuilder(...)] özniteliğinde belirtilen isme sahip olmalıdır.
  • Yöntem, doğrudan yapıcı türü üzerinde tanımlanmalıdır.
  • yöntemi staticolmalıdır.
  • Yöntemin, koleksiyon ifadesinin kullanıldığı yerde erişilebilir olması gerekir.
  • Yöntemin aritikliği, koleksiyon türünün aritikliği ile eşleşmelidir.
  • Yöntem, değerle geçirilen System.ReadOnlySpan<E>türünde tek bir parametreye sahip olmalıdır.
  • kimlik dönüştürmesi, örtük başvuru dönüştürmesi veya kutulama dönüştürme türünden koleksiyon türüne yöntem dönüşü vardır.

Temel türler veya arabirimler üzerinde bildirilen yöntemler yoksayılır ve CM kümesinin parçası değildir.

CM kümesi boşsa, koleksiyon türü öğe türüne sahip değildir ve oluşturma yöntemi yoktur. Aşağıdaki adımlardan hiçbiri geçerli değildir.

CM kümesindeki yöntemlerden yalnızca biri 'ten koleksiyon türünün E kimlik dönüşümünesahipse, bu, koleksiyon türü için oluşturma yöntemidir. Aksi takdirde, koleksiyon türünün bir oluşturma yöntemiyoktur.

[CollectionBuilder] özniteliği beklenen imzaya sahip çağrılabilen bir yönteme başvurmuyorsa bir hata bildirilir.

için, hedef türü olan bir koleksiyon ifadesinde, tür bildirimi C<S0, S1, …>C<T0, T1, …> ilişkili bir oluşturucu yöntemiB.M<U0, U1, …>()'ye sahipse, hedef türden gelen genel tür bağımsız değişkenleri () sırasıyla ve en dış kapsayıcı türden en içe doğru bu oluşturucu yöntemine uygulanır.

oluşturma yöntemi span parametresi açıkça scoped veya [UnscopedRef]olarak işaretlenebilir. parametresi örtük veya açıkça scopedise, derleyici yığın yerine yığın üzerindeki span için depolamayı ayırabilir.

Örneğin, için olası bir ImmutableArray<T>:

[CollectionBuilder(typeof(ImmutableArray), "Create")]
public struct ImmutableArray<T> { ... }

public static class ImmutableArray
{
    public static ImmutableArray<T> Create<T>(ReadOnlySpan<T> items) { ... }
}

Yukarıdaki oluşturma yöntemi, ImmutableArray<int> ia = [1, 2, 3]; şu şekilde dışa aktarılabilir:

[InlineArray(3)] struct __InlineArray3<T> { private T _element0; }

Span<int> __tmp = new __InlineArray3<int>();
__tmp[0] = 1;
__tmp[1] = 2;
__tmp[2] = 3;
ImmutableArray<int> ia =
    ImmutableArray.Create((ReadOnlySpan<int>)__tmp);

İnşaat

Koleksiyon ifadesinin öğeleri soldan sağa sırasıyla değerlendirilir. Her öğe tam olarak bir kez değerlendirilir ve öğelere yapılan diğer başvurular bu ilk değerlendirmenin sonuçlarına başvurur.

Bir spread öğesi, koleksiyon ifadesindeki sonraki öğeler değerlendirilmeden önce veya sonra yinelenmiş olabilir.

Oluşturma sırasında kullanılan yöntemlerden herhangi birinden atılan işlenmemiş bir istisna yakalanmadan kalır ve oluşturma sürecinde daha fazla adım atılmasını engeller.

Length, Countve GetEnumerator yan etkileri olmadığı varsayılır.


Hedef türü uygulayan bir yapı veya System.Collections.IEnumerable ise ve hedef türünbiroluşturma yöntemi yoksa, koleksiyon örneğinin yapısı aşağıdaki gibidir:

  • Öğeler sırayla değerlendirilir. Öğelerin bazıları veya tümü aşağıdaki adımlar sırasında, öncesinden ziyade değerlendirilebilir.

  • Derleyici, koleksiyon ifadesinin bilinen uzunluğunu, sayılabilir özellikleri — veya iyi bilinen arabirimlerden ya da türlerden eşdeğer özellikleri — her bir yayılan öğe ifadesinde çağırarak belirleyebilir.

  • Argümansız uygulanabilen oluşturucu çağrılır.

  • Sırayla her öğe için:

    • Öğe ,bir ifade öğesiyse, bağımsız değişken olarak Add öğesiyle uygun örneği veya uzantı yöntemi çağrılır. (Klasik koleksiyon başlatıcı davranışından farklı olarak, öğe değerlendirmesi ve Add çağrıları mutlaka birbirine karışmaz.)
    • bir yayılma elemanı ise, aşağıdakilerden biri kullanılır:
      • GetEnumerator üzerinde geçerli bir örneği veya uzantı yöntemi çağrılır ve numaralandırıcıdaki her öğe için, geçerli Add örneği veya uzantı yöntemi, öğeyi bağımsız değişken olarak kullanarak koleksiyon örneği üzerinde çağrılır. Numaralandırıcı IDisposableuygularsa, Dispose özel durumlardan bağımsız olarak numaralandırmadan sonra çağrılır.
      • AddRange üzerinde, bağımsız değişken olarak ifadesi'i içeren spread öğesiyle geçerli bir örneği veya uzantı yöntemi çağrılır.
      • Koleksiyon örneği ve CopyTo dizini bağımsız değişken olarak spread öğesi ifadesinde geçerli bir int örneği veya uzantı yöntemi çağrılır.
  • Yukarıdaki yapım adımları sırasında, geçerli bir EnsureCapacity örneği veya uzantı yöntemi olabilir ve bir veya daha fazla kez kapasite argümanı ile int koleksiyon örneği üzerinde çağrılabilir.


Hedef tür, bir dizisiise, bir aralığı, bir oluşturma yöntemine sahip bir türveya bir arabirimiise, koleksiyon örneğinin yapısı aşağıdaki gibidir:

  • Öğeler sırayla değerlendirilir. Öğelerin bazıları veya tümü, aşağıdaki adımlar yerine sırasında değerlendirilebilir.

  • Derleyici, her yayılan öğe ifadesinde, sayılabilir özelliklerini — veya iyi bilinen arabirimlerden veya türlerden eşdeğer özellikleri — çağırarak koleksiyon ifadesinin bilinen uzunluğunu belirleyebilir.

  • aşağıdaki gibi bir başlatma örneği oluşturulur:

    • Hedef tür bir dizi ise ve koleksiyon ifadesi bilinen uzunluğasahipse, beklenen uzunlukta bir dizi ayrılır.
    • Eğer hedef tür bir aralık veya oluşturma metoduolan bir türse ve koleksiyonun bilinen uzunluğuvarsa, bitişik depolamayı referans alarak beklenen uzunlukta bir aralık oluşturulur.
    • Aksi takdirde ara depolama ayrılır.
  • Sırayla her öğe için:

    • Öğe, birifade öğesi ise, değerlendirilen ifadeyi geçerli dizine eklemek için başlatma örneği dizinleyici çağrılır.
    • bir yayılma elemanı ise, aşağıdakilerden biri kullanılır:
      • İyi bilinen bir arabirim veya türün üyesi, öğeleri yayılım öğesi ifadesinden başlatma nesnesine kopyalamak için çağrılır.
      • Geçerli bir GetEnumerator örneği veya uzantı yöntemi, spread öğesi ifadesi üzerinde çağrılır. Numalandırıcıdaki her öğe için, öğe mevcut dizine eklenirken, başlatma örneği dizinleyici çağrılır. Numaralandırıcı IDisposableuygularsa, Dispose özel durumlardan bağımsız olarak numaralandırmadan sonra çağrılır.
      • Geçerli bir CopyTo örneği veya uzantı yöntemi, başlatma örneği ve dizini bağımsız değişkenler olarak int üzerinde çağrılır.
  • Ara depolama alanı koleksiyon için ayrılmışsa, gerçek koleksiyon uzunluğuyla bir koleksiyon örneği ayrılır ve başlatma örneğindeki değerler koleksiyon örneğine kopyalanır veya bir aralık gerekiyorsa, derleyici , ara depolama alanından gerçek koleksiyon uzunluğunda bir aralığı kullanabilir. Aksi takdirde başlatma örneği koleksiyon örneğidir.

  • Hedef türü oluşturma yöntemine sahipse, oluşturma yöntemi span örneğiyle çağrılır.


Not: Derleyici, koleksiyona öğe eklemeyi geciktirebilir veya sonraki öğeleri değerlendirene kadar yayma öğeleri üzerinde yinelemeyi geciktirebilir. (Sonraki yayma öğeleri, koleksiyonu ayırmadan önce koleksiyonun beklenen uzunluğunu hesaplamaya izin verecek sayılabilir özelliklerine sahip olduğunda.) Buna karşılık, derleyici hevesle hem koleksiyona öğe ekleyebilir hem de gecikmenin bir avantajı olmadığında yayma öğeleri arasında hevesle yineleyebilir.

Aşağıdaki koleksiyon ifadesini göz önünde bulundurun:

int[] x = [a, ..b, ..c, d];

b ve c öğeleri sayılabilir ise, derleyici a değerlendirilinceye kadar b ve c öğelerinin eklenmesini geciktirebilir, böylece sonuçta elde edilen dizinin beklenen uzunlukta tahsis edilmesine olanak tanıyabilir. Bundan sonra, derleyici cdeğerlendirmeden önce döğelerinden öğeleri hızlıca ekleyebilir.

var __tmp1 = a;
var __tmp2 = b;
var __tmp3 = c;
var __result = new int[2 + __tmp2.Length + __tmp3.Length];
int __index = 0;
__result[__index++] = __tmp1;
foreach (var __i in __tmp2) __result[__index++] = __i;
foreach (var __i in __tmp3) __result[__index++] = __i;
__result[__index++] = d;
x = __result;

Boş koleksiyon literalı

  • Boş değişmez değer []'nin bir türü yok. Ancak, nullbenzer şekilde herhangi bir oluşturulabilir koleksiyon türüne otomatik olarak dönüştürülebilir.

    Örneğin, hedef türü olmadığı ve başka dönüştürmeler olmadığı için aşağıdakiler yasal değildir:

    var v = []; // illegal
    
  • Boş bir değişmez değerin yayılmasının atlanmasına izin verilir. Örneğin:

    bool b = ...
    List<int> l = [x, y, .. b ? [1, 2, 3] : []];
    

    Burada, b yanlış ise, son değişmez değerde sıfır değerlere hemen yayılacağından, boş koleksiyon ifadesi için herhangi bir değerin gerçekten oluşturulmasına gerek yoktur.

  • Boş koleksiyon ifadesinin, değiştirilebilir olmadığı bilinen son bir koleksiyon değeri oluşturmak için kullanılırsa tekli olmasına izin verilir. Mesela:

    // Can be a singleton, like Array.Empty<int>()
    int[] x = []; 
    
    // Can be a singleton. Allowed to use Array.Empty<int>(), Enumerable.Empty<int>(),
    // or any other implementation that can not be mutated.
    IEnumerable<int> y = [];
    
    // Must not be a singleton.  Value must be allowed to mutate, and should not mutate
    // other references elsewhere.
    List<int> z = [];
    

Başvuru güvenliği

güvenli bağlam kısıtlaması için şu güvenli bağlam değerlerinin tanımlarına bakın: bildirim bloğu, işlev üyesive çağıran bağlam.

Bir koleksiyon ifadesinin güvenli bağlam şudur:

  • Boş bir koleksiyon ifadesinin [] güvenli bağlamı, çağıran bağlamı.

  • Hedef tür span türüyse ve T, temel türlerinden biriyse ve koleksiyon ifadesi yalnızca sabit değerler içeriyorsa, koleksiyon ifadesinin güvenli bağlamı çağıran bağlamdır.

  • Hedef tür, yayılma türüSystem.Span<T> veya System.ReadOnlySpan<T>ise, koleksiyon ifadesinin güvenli bağlamı, bildirim bloğuolur.

  • Hedef tür bir ref yapı türü ve bir oluşturma yöntemi içeriyorsa, koleksiyon ifadesinin güvenli bağlamı, koleksiyon ifadesinin yöntemin span argümanı olduğu, oluşturma yönteminin bir çağrısının güvenli bağlamıdır.

  • Aksi takdirde, koleksiyon ifadesinin güvenli bağlamı çağıran bağlamıdır.

bildirim bloğu güvenli bağlamına sahip bir koleksiyon ifadesi, içinde bulunduğu kapsamdan dışarı çıkamaz ve derleyici koleksiyonu yığın yerine bellekte depolayabilir.

Bir ref struct türünün koleksiyon ifadesinin bildirim bloğundandışarı çıkmasına izin vermek için, ifadenin başka bir türe dönüştürülmesi gerekebilir.

static ReadOnlySpan<int> AsSpanConstants()
{
    return [1, 2, 3]; // ok: span refers to assembly data section
}

static ReadOnlySpan<T> AsSpan2<T>(T x, T y)
{
    return [x, y];    // error: span may refer to stack data
}

static ReadOnlySpan<T> AsSpan3<T>(T x, T y, T z)
{
    return (T[])[x, y, z]; // ok: span refers to T[] on heap
}

Tür çıkarımı

var a = AsArray([1, 2, 3]);          // AsArray<int>(int[])
var b = AsListOfArray([[4, 5], []]); // AsListOfArray<int>(List<int[]>)

static T[] AsArray<T>(T[] arg) => arg;
static List<T[]> AsListOfArray<T>(List<T[]> arg) => arg;

tür çıkarımı kuralları aşağıdaki gibi güncelleştirilir.

birinci aşama için mevcut kurallar yeni bir giriş türü çıkarımı bölümüne ayıklanır ve giriş türü çıkarımı ile koleksiyon ifadeleri için çıkış türü çıkarımı kuralı eklenir.

11.6.3.2 İlk aşama

her bir yöntem argümanı için Eᵢ:

  • Giriş türü çıkarımı karşılık gelen parametre türüne'den Eᵢyapılır.

Bir giriş türü çıkarımı, bir ifadeden E bir türe T yapılır:

  • E öğeleri Eᵢ ile bir koleksiyon ifadesiyse ve T Türü öğe türüne sahip bir türse veya T null atanabilir bir değer türüyse ve T0öğe türüne sahipse, her Eᵢ için:
    • Eᵢ birifade öğesiyse, 'denEᵢ'aTₑ yapılır.
    • Eᵢ, yayma öğesi,Sᵢyineleme türüne sahipse, SᵢTₑ yapılır.
  • [birinci aşamadan itibaren mevcut kurallar] ...

11.6.3.7 Çıkış türü çıkarımları

Bir çıkış türü çıkarımı, aşağıdaki şekilde bir ifadeden bir türe ET yapılır:

  • E öğeleriyle Eᵢ bir koleksiyon ifadesiyse ve TTöğe türüne sahip bir türse T0?değer türüyse ve T0Tₑöğe türüne sahipse, her Eᵢiçin :
    • Eğer Eᵢ, birifade öğesiyse, o zaman Tₑyapılır.
    • Eᵢ bir yayma öğesiyse, Eᵢ'den çıkarım yapılmaz.
  • [çıkış türü çıkarımlarından mevcut kurallar] ...

Uzantı yöntemleri

uzantı yöntemi çağırma kurallarında değişiklik yok.

12.8.10.3 Uzantı yöntemi çağrıları

Uzantı yöntemi Cᵢ.Mₑ, uygun olarak ise:

  • ...
  • ifadesi, Mₑ'nin ilk parametresinin türüne örtük bir kimlik, referans veya kutulama dönüşümü sağlar.

Koleksiyon ifadesinin doğal bir türü olmadığından, türünden türüne olan mevcut dönüşümler uygulanamaz. Sonuç olarak, bir koleksiyon ifadesi doğrudan bir uzantı yöntemi çağırma için ilk parametre olarak kullanılamaz.

static class Extensions
{
    public static ImmutableArray<T> AsImmutableArray<T>(this ImmutableArray<T> arg) => arg;
}

var x = [1].AsImmutableArray();           // error: collection expression has no target type
var y = [2].AsImmutableArray<int>();      // error: ...
var z = Extensions.AsImmutableArray([3]); // ok

Aşırı yükleme çözümü

İfade, koleksiyon ifadesi dönüşümlerinde belirli hedef türleri tercih etmek için daha iyi bir dönüşüm sağlamak üzere güncellendi.

Güncelleştirilmiş kurallarda:

  • span_type bunlardan biridir:
    • System.Span<T>
    • System.ReadOnlySpan<T>.
  • array_or_array_interface bunlardan biridir:
    • dizi türü
    • dizi türütarafından uygulanan aşağıdaki arabirim türlerinden biri:
      • System.Collections.Generic.IEnumerable<T>
      • System.Collections.Generic.IReadOnlyCollection<T>
      • System.Collections.Generic.IReadOnlyList<T>
      • System.Collections.Generic.ICollection<T>
      • System.Collections.Generic.IList<T>

Bir ifade 'den tür 'ye dönüştüren örtük dönüştürme ve bir ifade 'den tür 'ye dönüştüren örtük dönüştürme göz önünde bulundurulduğunda, eğer aşağıdakilerden biri geçerliyse daha iyi bir dönüşüm olur:

  • E, bir koleksiyon ifadesidir ve aşağıdakilerden biridir:
    • T₁ System.ReadOnlySpan<E₁>, ve T₂System.Span<E₂>, ve E₁'dan E₂'a örtük bir dönüşüm vardır
    • T₁, System.ReadOnlySpan<E₁> veya System.Span<E₁>'tür ve T₂'dan 'a örtük dönüştürme vardır. E₂ ise E₂öğe türüne sahip bir array_or_array_interface'dur.
    • T₁ bir span_typedeğildir ve T₂ bir span_typedeğildir ve T₁'den T₂'a örtük bir dönüştürme vardır.
  • E bir koleksiyon ifadesi değildir ve aşağıdakilerden biri geçerlidir:
    • E T₁ ile tam olarak eşleşir ve E tam olarak T₂ ile eşleşmez.
    • E, hem T₁ hem de T₂ile veya hiçbirisiyle tam olarak eşleşir ve T₁, 'den daha iyi bir dönüştürme hedefi olarak T₂ olur.
  • E bir yöntem grubudur, ...

Dizi başlatıcıları ve koleksiyon ifadeleri arasında aşırı yükleme çözümlemesi ile farklılıklara örnekler:

static void Generic<T>(Span<T> value) { }
static void Generic<T>(T[] value) { }

static void SpanDerived(Span<string> value) { }
static void SpanDerived(object[] value) { }

static void ArrayDerived(Span<object> value) { }
static void ArrayDerived(string[] value) { }

// Array initializers
Generic(new[] { "" });      // string[]
SpanDerived(new[] { "" });  // ambiguous
ArrayDerived(new[] { "" }); // string[]

// Collection expressions
Generic([""]);              // Span<string>
SpanDerived([""]);          // Span<string>
ArrayDerived([""]);         // ambiguous

Span türleri

ReadOnlySpan<T> ve Span<T> span türleri,yapılılabilir koleksiyon türleridir. Onlar için destek, params Span<T>tasarımının izindedir. Özellikle, bu span'lardan herhangi birinin oluşturulması, eğer params dizisi derleyici tarafından ayarlanan sınırlar içerisindeyse, yığında oluşturulan bir T[] dizisiyle sonuçlanır. Aksi takdirde dizi yığın belleğine tahsis edilir.

Derleyici yığında ayırmayı seçerse, bir literal'in doğrudan belirli bir noktada bir stackalloc'a çevrilmesi gerekli değildir. Örneğin, şu durumlarda:

foreach (var x in y)
{
    Span<int> span = [a, b, c];
    // do things with span
}

stackalloc anlamı aynı kaldığı ve Span korunduğunu sürece derleyicinin bunu kullanarak çevirmesine izin verilir. Örneğin, yukarıdakini şu şekilde çevirebilir:

Span<int> __buffer = stackalloc int[3];
foreach (var x in y)
{
    __buffer[0] = a
    __buffer[1] = b
    __buffer[2] = c;
    Span<int> span = __buffer;
    // do things with span
}

Derleyici, mevcutsa, yığın üzerinde ayırmayı seçtiğinde satır içi dizileride kullanabilir. C# 12'de satır içi dizilerin bir koleksiyon ifadesiyle başlatılamayacağını unutmayın. Bu özellik açık bir tekliftir.

Derleyici yığın üzerinde ayırmaya karar verirse, Span<T>'ın çevirisi basitçe:

T[] __array = [...]; // using existing rules
Span<T> __result = __array;

Koleksiyon birebir çevirisi

Bir koleksiyon ifadesi, koleksiyon ifadesindeki her yayma öğesinin derleme zamanı türü sayılabilir olduğunda bilinen bir uzunluğa sahiptir.

Arabirim çevirisi

Değişmez arayüz çevirisi

Değiştirilebilen üyeler içermeyen, yani IEnumerable<T>, IReadOnlyCollection<T>ve IReadOnlyList<T>gibi, bir hedef türü verildiğinde, bu arabirimi uygulayan bir değer üretmek için uyumlu bir uygulama gerekir. Bir tür sentezlenmişse, hangi arabirim türünün hedeflendiğine bakılmaksızın sentezlenmiş türün tüm bu arabirimlerin yanı sıra ICollection<T> ve IList<T>uygulaması önerilir. Bu, bir değer tarafından uygulanan arabirimleri inceleyenler de dahil olmak üzere, mevcut kütüphanelerle maksimum uyumluluğu ve performans iyileştirmelerinin etkinleştirilmesini sağlar.

Buna ek olarak, değerin genel olmayan (nongeneric) ICollection ve IList arabirimlerini uygulaması gerekir. Bu, koleksiyon ifadelerinin veri bağlama gibi senaryolarda dinamik iç gözlemi desteklemesini sağlar.

Uyumlu bir uygulama aşağıdakiler için ücretsizdir:

  1. Gerekli arabirimleri uygulayan mevcut bir türü kullanın.
  2. Gerekli arabirimleri uygulayan bir türü sentezler.

Her iki durumda da, kullanılan türün kesinlikle gerekli olandan daha büyük bir arabirim kümesi uygulamasına izin verilir.

Sentezlenmiş türler, gerekli arabirimleri düzgün bir şekilde uygulamak istedikleri herhangi bir stratejiyi kullanmakta serbesttir. Örneğin, sentezlenmiş bir tür, öğeleri doğrudan kendi içine dahil ederek ek iç koleksiyon tahsislerine gerek kalmayabilir. Sentezlenmiş bir tür, değerleri doğrudan hesaplamayı tercih ederek herhangi bir depolama alanı da kullanamaz. Örneğin, index + 1[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]için döndürmek.

  1. true (uygulandıysa) ve nongeneric ICollection<T>.IsReadOnly ve IList.IsReadOnlyiçin sorgulandığında değer IList.IsFixedSize döndürmelidir. Bu, tüketicilerin değiştirilebilir görünümleri uygulamalarına rağmen koleksiyonun değişmez olduğunu uygun bir şekilde anlayabilmesini sağlar.
  2. Değerin bir mutasyon yöntemine yapılan herhangi bir çağrıda (IList<T>.Addgibi) atması gerekir. Bu, güvenlik sağlayarak değiştirilemez bir koleksiyonun yanlışlıkla değiştirilmesini önler.

Değiştirilebilir arabirim çevirisi

Verilen, değişen üyeler içeren ICollection<T> veya IList<T>hedef türü:

  1. Değerin List<T>bir örneği olması gerekir.

Bilinen uzunluk çevirisi

Bilinen uzunluğa sahip, verilerin kopyalanmaması ve sonuçta gereksiz boşluk olmaması olasılığına sahip bir sonucun verimli bir şekilde oluşturulmasını sağlar.

Bilinen uzunluğu olmaması, herhangi bir sonucun oluşturulmasını engellemez. Ancak, verilerin üretilip son hedefe taşınması ekstra CPU ve bellek maliyetlerine neden olabilir.

  • Belirli bir uzunluk literal [e1, ..s1, etc] için, çeviri öncelikle aşağıdakilerle başlar:

    int __len = count_of_expression_elements +
                __s1.Count;
                ...
                __s_n.Count;
    
  • Bu değişmez değer için hedef türü T olarak belirlenmiştir.

    • T bir T1[]ise, literal şu şekilde çevrilir:

      T1[] __result = new T1[__len];
      int __index = 0;
      
      __result[__index++] = __e1;
      foreach (T1 __t in __s1)
          __result[__index++] = __t;
      
      // further assignments of the remaining elements
      

      Uygulamanın diziyi doldurmak için diğer araçları kullanmasına izin verilir. Örneğin, .CopyTo()gibi verimli toplu kopyalama yöntemlerini kullanma.

    • T bazı Span<T1>ise, bu durumda literal ifade yukarıdakiyle aynı şekilde çevrilir, ancak __result başlatma şu şekilde çevrilir:

      Span<T1> __result = new T1[__len];
      
      // same assignments as the array translation
      

      Çeviri, new T1[] yerine span-safety korunursa stackalloc T1[] veya satır içi dizi kullanabilir.

    • T bazı ReadOnlySpan<T1>ise, Span<T1> durumu için olduğu gibi 'literal' aynı şekilde çevrilir; ancak nihai sonuç, Span<T1>'ünaracılığıyla'ten ReadOnlySpan<T1>'ya örtük olarak dönüştürülmesi olacaktır.

      ReadOnlySpan<T1> bazı temel türler olduğu ve tüm koleksiyon öğeleri sabit olduğu bir T1'ın verilerinin kupette veya yığında bulunmasına gerek yoktur. Örneğin, bir uygulama bu aralığı doğrudan programın veri segmentinin bir kısmına başvuru olarak oluşturabiliyor.

      Yukarıdaki formlar (diziler ve yayılma alanları için) koleksiyon ifadesinin temel gösterimleridir ve aşağıdaki çeviri kuralları için kullanılır:

      • Eğer T ilgili C<S0, S1, …> bir create-methodB.M<U0, U1, …>() içeriyorsa, literali şu şekilde çevrilir:

        // Collection literal is passed as is as the single B.M<...>(...) argument
        C<S0, S1, …> __result = B.M<S0, S1, …>([...])
        

        oluşturma metodu'in, belirli bir örneklenmiş ReadOnlySpan<T>'nin bağımsız değişken türüne sahip olması gerektiğinden, koleksiyon ifadesi oluşturma metoduna geçirildiğinde aralıklar için çeviri kuralı uygulanır.

      • T koleksiyon başlatıcılarını destekliyorsa, o zaman:

        • tür T, int capacitytek parametreli erişilebilir bir oluşturucu içeriyorsa, değişmez değer şu şekilde çevrilir:

          T __result = new T(capacity: __len);
          __result.Add(__e1);
          foreach (var __t in __s1)
              __result.Add(__t);
          
          // further additions of the remaining elements
          

          Not: parametresinin adının capacityolması gerekir.

          Bu form, iç depolamanın verimli bir şekilde ayrılması için yeni oluşturulan türün öğe sayısını belirleyen bir literal'ın kullanımına olanak tanır. Bu, öğeler eklendikçe gereksiz yeniden yerleştirmeleri önler.

        • aksi takdirde, literal anlamı şu şekilde çevrilmiştir:

          T __result = new T();
          
          __result.Add(__e1);
          foreach (var __t in __s1)
              __result.Add(__t);
          
          // further additions of the remaining elements
          

          Bu, depolama alanının iç olarak yeniden taşınmasını önlemek için kapasite iyileştirmesi olmasa da hedef türün oluşturulmasına olanak tanır.

Bilinmeyen uzunluk çevirisi

  • T değişmez değeri için hedef türü verilen:

    • T koleksiyon başlatıcılarınıdestekliyorsa, literal şu şekilde çevrilir:

      T __result = new T();
      
      __result.Add(__e1);
      foreach (var __t in __s1)
          __result.Add(__t);
      
      // further additions of the remaining elements
      

      Bu, mümkün olan en az iyileştirme miktarına sahip olsa da, yinelenebilir herhangi bir türün yayılmasına olanak tanır.

    • T bir T1[]ise, literal, aşağıdakilerle aynı anlamsal özelliğe sahiptir.

      List<T1> __list = [...]; /* initialized using predefined rules */
      T1[] __result = __list.ToArray();
      

      Yukarıda belirtilenler verimsiz olmasına rağmen, aracı liste oluşturulur ve ardından bundan son dizinin bir kopyası oluşturulur. Uygulamalar bunu iyileştirmekte serbesttir, örneğin aşağıdaki gibi kodlar üretebilirsiniz:

      T1[] __result = <private_details>.CreateArray<T1>(
          count_of_expression_elements);
      int __index = 0;
      
      <private_details>.Add(ref __result, __index++, __e1);
      foreach (var __t in __s1)
          <private_details>.Add(ref __result, __index++, __t);
      
      // further additions of the remaining elements
      
      <private_details>.Resize(ref __result, __index);
      

      Bu, kitaplık koleksiyonlarının ek yükü olmadan minimum atık ve kopyalamaya olanak tanır.

      CreateArray'a gönderilen sayımlar, israflı yeniden boyutlandırmaları önlemek için başlangıç boyutunu belirlemek amacıyla kullanılır.

    • T bir türse, bir uygulama yukarıdaki T[] stratejisini veya aynı semantiğe sahip başka herhangi bir stratejiyi takip edebilir, ancak daha iyi performans gösterebilir. Örneğin, diziyi liste öğelerinin bir kopyası olarak bölmek yerine, CollectionsMarshal.AsSpan(__list) doğrudan bir span değeri almak için kullanılabilir.

Desteklenmeyen senaryolar

Koleksiyon ifadeleri birçok senaryo için kullanılabilse de, yerini alamayacakları bazı senaryolar vardır. Bunlar şunlardır:

  • Çok boyutlu diziler (örneğin, new int[5, 10] { ... }). Boyutları dahil etme özelliği yoktur ve tüm koleksiyon sabit değerleri yalnızca doğrusal veya harita yapılarıdır.
  • Özel değerleri oluşturucularına geçiren koleksiyonlar. Kullanılan oluşturucuya erişim imkanı yok.
  • İç içe koleksiyon başlatıcıları, örneğin new Widget { Children = { w1, w2, w3 } }. Children = [w1, w2, w3]'den çok farklı semantiklere sahip olduğundan bu formun kalması gerekir. Önceki, .Add üzerinde .Children'ı tekrar tekrar çağırırken, ikincisi .Childrenüzerine yeni bir koleksiyon atar. .Children atanamıyorsa, ikinci formun mevcut koleksiyona eklemeye geri dönmesini sağlayabiliriz, ancak bu son derece kafa karıştırıcı olabilir.

Söz dizimi belirsizlikleri

  • iki "gerçek" söz dizimsel belirsizlik vardır, yani collection_literal_expressionkullanan kod için birden çok geçerli söz dizimi yorumu bulunmaktadır.

    • spread_element range_expressionile belirsizdir. Teknik olarak şu durumlar oluşabilir:

      Range[] ranges = [range1, ..e, range2];
      

      Bu sorunu çözmek için şunları yapabilirsiniz:

      • Kullanıcıların bir aralık istemesi durumunda, (..e)'ı parantez içine almalarını veya başlangıç dizini 0..e'i eklemelerini zorunlu tutun.
      • Yayılma için farklı bir söz dizimi (...gibi) seçin. Bu durum, dilim desenleriyle tutarlılığın olmamasından dolayı talihsiz bir durum olacaktır.
  • Gerçek bir belirsizliğin olmadığı ancak söz diziminin ayrıştırma karmaşıklığını büyük ölçüde artırdığı iki durum vardır. Mühendislik süresi verildiğinde sorun olmasa da, bu durum koda bakarken kullanıcılar için bilişsel ek yükü artırmaya devam eder.

    • ifadeler veya yerel işlevlerde collection_literal_expression ve attributes arasındaki belirsizlik. Düşün

      [X(), Y, Z()]
      

      Bu, aşağıdakilerden biri olabilir:

      // A list literal inside some expression statement
      [X(), Y, Z()].ForEach(() => ...);
      
      // The attributes for a statement or local function
      [X(), Y, Z()] void LocalFunc() { }
      

      Karmaşık bir öngörü olmadan, değişmez değerin tamamını tüketmeden anlamak imkansız olurdu.

      Buna yönelik seçenekler şunlardır:

      • Bu durumun hangisi olduğunu belirlemek için ayrıştırma çalışması yaparak buna izin verin.
      • Bu işlemi engelleyin ve kullanıcıdan sabit değeri ([X(), Y, Z()]).ForEach(...)gibi parantez içinde yazmasını isteyin.
      • collection_literal_expression ile conditional_expression ve null_conditional_operationsarasındaki belirsizlik. Düşün
      M(x ? [a, b, c]
      

      Bu, aşağıdakilerden biri olabilir:

      // A ternary conditional picking between two collections
      M(x ? [a, b, c] : [d, e, f]);
      
      // A null conditional safely indexing into 'x':
      M(x ? [a, b, c]);
      

      Karmaşık bir öngörü olmadan, değişmez değerin tamamını tüketmeden anlamak imkansız olurdu.

      Not: Bu, doğal tür olmasa bile bir sorundur çünkü hedef yazma conditional_expressionsaracılığıyla uygulanır.

      Diğerlerinde olduğu gibi, belirsizlikleri gidermek için ayraçlara ihtiyaç duyabiliriz. Başka bir deyişle, şöyle yazılmadığı sürece null_conditional_operation yorumunu varsayın: x ? ([1, 2, 3]) :. Ancak, bu oldukça talihsiz görünüyor. Bu tür bir kodun yazması mantıksız görünmüyor ve büyük olasılıkla insanları dolandıracak.

Dezavantaj -ları

  • Bu, zaten sahip olduğumuz sayısız yolun üzerine, koleksiyon ifadeleri için başka bir form tanıtır. Bu, dil için ek karmaşıklıktır. Bununla birlikte, bu, tümünü yönetmek için tek bir halka söz diziminde birleştirmeyi de mümkün kılar; bu da mevcut kod temellerinin basitleştirilebileceği ve her yerde tekdüzen bir görünüme taşınabileceği anlamına gelir.
  • [...] yerine {...} kullanmak, diziler ve koleksiyon başlatıcılar için zaten genel olarak kullandığımız söz diziminden uzaklaşır. Özellikle [...]yerine {...} kullanır. Ancak bu, liste desenlerini yaptığımızda dil ekibi tarafından zaten ele alınmıştı. {...} ile liste kalıplarına uyum sağlamayı denedik ve aşılamaz sorunlarla karşılaştık. Bu nedenle, C# için yeni olan ama birçok programlama dilinde doğal hissettiren ve belirsizlik olmadan yeni bir başlangıç yapmamıza izin veren [...]'e geçtik. Karşılık gelen sabit form olarak [...] kullanmak, en son kararlarımızla uyumludur ve bize sorunsuz bir çalışma ortamı sağlar.

Bu, dile kusurlar katıyor. Örneğin, aşağıdakiler hem yasaldır hem de (neyse ki) tam olarak aynı anlama gelir:

int[] x = { 1, 2, 3 };
int[] x = [ 1, 2, 3 ];

Ancak, yeni değişmez söz diziminin getirdiği genişlik ve tutarlılık göz önünde bulundurulduğunda, kişilerin yeni forma geçmelerini önermeyi düşünmeliyiz. IDE önerileri ve düzeltmeleri bu konuda yardımcı olabilir.

Alternatif

  • Başka hangi tasarımlar göz önünde bulunduruldu? Bunu yapmamanın etkisi nedir?

Çözülen sorular

  • Derleyici, stackalloc kullanılabilir olmadığında ve yineleme türü ilkel bir tür olduğunda yığın ayırma için kullanmalıdır?

    Karar: Hayır. stackalloc arabelleğinin yönetilmesi, koleksiyon ifadesi döngü içinde olduğunda arabelleğin tekrar tekrar ayrılmadığından emin olmak için satır içi dizi üzerinde ek çaba gerektirir. Derleyicideki ve oluşturulan koddaki ek karmaşıklık, eski platformlarda yığın ayırma avantajından daha fazladır.

  • Uzunluk/Sayı özellik değerlendirmesiyle karşılaştırıldığında somut öğeleri hangi sırada değerlendirmeliyiz? Önce tüm öğeleri, sonra tüm uzunlukları değerlendirmeli miyiz? Yoksa bir öğeyi, sonra uzunluğunu, sonra sonraki öğeyi vb. değerlendirmeli miyiz?

    Çözüm: Önce tüm öğeleri değerlendiririz, sonra diğer her şey bunu izler.

  • bilinmeyen uzunluk sabiti, dizi, span veya Yapı(dizi/span) koleksiyonu gibi bilinen uzunluktaolması gereken bir koleksiyon türü oluşturabilir mi? Bunu verimli bir şekilde yapmak zor olabilir, ancak havuzlanmış diziler ve/veya yapı kurucuların akıllıca kullanılmasıyla mümkün olabilir.

    Karar: Evet, bilinmeyen uzunlukta bir 'dan sabit uzunlukta bir koleksiyon oluşturmaya izin veririz. Derleyicinin bunu mümkün olduğunca verimli bir şekilde uygulamasına izin verilir.

    Bu konunun özgün tartışmasını kaydetmek için aşağıdaki metin vardır.

    Kullanıcılar, kod gibi yöntemlerle bilinmeyen bir uzunluktaki sabit değeri, bilinen bir uzunluğa dönüştürebilir.

    ImmutableArray<int> x = [a, ..unknownLength.ToArray(), b];
    

    Ancak, geçici depolama ayırmalarını zorlama gereğinden dolayı bu talihsiz bir durumdur. Bunun nasıl yayıldığını denetleseydik daha verimli olabilirdik.

  • collection_expression, bir IEnumerable<T> veya diğer koleksiyon arabirimleri için hedef türü olarak kullanılabilir mi?

    Örneğin

    void DoWork(IEnumerable<long> values) { ... }
    // Needs to produce `longs` not `ints` for this to work.
    DoWork([1, 2, 3]);
    

    Çözüm: Evet, bir sabit, I<T>'in uyguladığı herhangi bir arabirim türü olan List<T>'a hedef tür olarak atanabilir. Örneğin, IEnumerable<long>. Bu, List<long> hedef yazma ve ardından bu sonucu belirtilen arabirim türüne atama ile aynıdır. Bu konunun özgün tartışmasını kaydetmek için aşağıdaki metin vardır.

    Burada açık soru, gerçekte hangi temel türün oluşturulacağını belirlemektir. Seçeneklerden biri, params IEnumerable<T>teklifine bakmaktır. Burada, params T[]ile gerçekleşene benzer şekilde değerleri geçirmek için bir dizi oluştururuz.

  • Derleyici Array.Empty<T>()için [] üretebilir mi/üretmeli mi? Mümkün olduğunda ayırmalardan kaçınmak için bunu yapması zorunlu kılınmalı mı?

    Evet. Derleyici, bunun yasal olduğu ve nihai sonucun değiştirilemez olduğu her durumda Array.Empty<T>() yaymalıdır. Örneğin, T[], IEnumerable<T>, IReadOnlyCollection<T> veya IReadOnlyList<T>etiketlerini hedefleme. Hedef değiştirilebilir olduğunda (Array.Empty<T> veya ICollection<T>) IList<T> kullanmamalıdır.

  • En yaygın AddRange yöntemini aramak için koleksiyon başlatıcılarını genişletmeli miyiz? İnşa edilen temel tür, yayma elemanlarının potansiyel olarak daha verimli bir şekilde eklenmesini sağlamak için kullanılabilir. .CopyTo gibi şeyleri de aramak isteyebiliriz. Bu yöntemler, çevrilen kodda doğrudan listeleme yerine gereksiz bellek tahsisi/gönderimine neden olabileceğinden burada dezavantajlar olabilir.

    Evet. Bir uygulamanın, bu yöntemlerin iyi tanımlanmış semantiği olduğu ve koleksiyon türlerinin "iyi davranılmış" olması gerektiği varsayımı altında bir koleksiyon değeri başlatmak için diğer yöntemleri kullanmasına izin verilir. Ancak uygulamada bir uygulamanın dikkatli olması gerekir çünkü tek yönlü avantajlar (toplu kopyalama) olumsuz sonuçlar da doğurabilir (örneğin, yapı koleksiyonunu kutulama).

    Bir uygulama, dezavantajların olmadığı durumlarda yararlanmalıdır. Örneğin, bir .AddRange(ReadOnlySpan<T>) yöntemiyle.

Çözülmemiş sorular

  • yineleme türü "belirsiz" olduğunda (bazı tanımlara göre) öğe türünün çıkarmasına izin vermeli miyiz? Örneğin:
Collection x = [1L, 2L];

// error CS1640: foreach statement cannot operate on variables of type 'Collection' because it implements multiple instantiations of 'IEnumerable<T>'; try casting to a specific interface instantiation
foreach (var x in new Collection) { }

static class Builder
{
    public Collection Create(ReadOnlySpan<long> items) => throw null;
}

[CollectionBuilder(...)]
class Collection : IEnumerable<int>, IEnumerable<string>
{
    IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null;
    IEnumerator<string> IEnumerable<string>.GetEnumerator() => throw null;
    IEnumerator IEnumerable.GetEnumerator() => throw null;
}
  • Koleksiyon literal'i oluşturmak ve hemen dizine eklemek yasal olmalı mı? Not: Bu, koleksiyon değişmez değerlerinin doğal türü olup olmadığına ilişkin aşağıdaki çözümlenmemiş soruya yanıt gerektirir.

  • Büyük koleksiyonlar için yığın üzerinde yapılan bellek tahsisleri yığının taşmasına neden olabilir. Derleyicinin bu verileri yığın üzerinde yerleştirmek için buluşsal bir yöntemi olması gerekir mi? Bu esnekliğe izin vermek için dil belirtilmemiş olmalıdır? params Span<T>için spesifikasyonu takip etmeliyiz.

  • spread_elementtürünü hedeflememiz gerekiyor mu? Örneğin, şunları göz önünde bulundurun:

    Span<int> span = [a, ..b ? [c] : [d, e], f];
    

    Not: Bu durum genellikle, bazı öğe kümelerinin koşullu olarak eklenmesine izin vermek veya koşul yanlışsa hiçbir şey eklenmemesi için aşağıdaki biçimde karşımıza çıkabilir.

    Span<int> span = [a, ..b ? [c, d, e] : [], f];
    

    Bu tam sabiti değerlendirmek için, içindeki öğe ifadelerini değerlendirmemiz gerekir. Bu, b ? [c] : [d, e]değerlendirebilmek anlamına gelir. Bununla birlikte, bu ifadeyi bağlamında değerlendirmek için bir hedef türü yoksa ve herhangi bir doğal tür yoksa, burada [c] veya [d, e] ile ne yapacağımızı belirleyemeyiz.

    Bunu çözmek için, değişmez değerin spread_element ifadesini değerlendirirken, değişmez değerin kendisinin hedef türüne eşdeğer örtük bir hedef türü olduğunu söyleyebiliriz. Bu nedenle yukarıdaki ifade şu şekilde yeniden yazılacak:

    int __e1 = a;
    Span<int> __s1 = b ? [c] : [d, e];
    int __e2 = f;
    
    Span<int> __result = stackalloc int[2 + __s1.Length];
    int __index = 0;
    
    __result[__index++] = a;
    foreach (int __t in __s1)
      __result[index++] = __t;
    __result[__index++] = f;
    
    Span<int> span = __result;
    

oluşturulabilir koleksiyon türünün, oluşturma yöntemi ile kullanılması, dönüştürmenin sınıflandırıldığı bağlama duyarlıdır.

Dönüştürmenin bu durumda varlığı, koleksiyon türünün yineleme türü kavramına bağlıdır. Eğer , ReadOnlySpan<T>iterasyon türü olan bir T alan bir oluşturma yöntemi varsa, dönüştürme vardır. Aksi takdirde, olmaz.

Ancak, yineleme türüforeach gerçekleştirildiği bağlama duyarlıdır. Aynı koleksiyon türü kapsam içindeki uzantı yöntemlerine göre farklı olabilir ve tanımsız da olabilir.

Tür, kendi başına foreach ile dolaşılabilir olacak şekilde tasarlanmamışsa, foreach amacıyla yeterince uygundur. Bu durumda uzantı yöntemleri, bağlam ne olursa olsun türün foreach-ed biçimini değiştiremez.

Ancak, bir dönüştürmenin bu şekilde bağlama duyarlı olması biraz garip geliyor. Dönüşüm aslında "kararsız" olur. koleksiyon türü açıkça oluşturulabilir olarak tasarlanmış bir koleksiyon türünün çok önemli bir ayrıntının tanımını (yineleme türü) dışarıda bırakmasına izin verilir. "Çevrilemez" türünü olduğu gibi bırakmak.

Aşağıda bir örnek verilmiştir:

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

[CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))]
class MyCollection
{
}
class MyCollectionBuilder
{
    public static MyCollection Create(ReadOnlySpan<long> items) => throw null;
    public static MyCollection Create(ReadOnlySpan<string> items) => throw null;
}

namespace Ns1
{
    static class Ext
    {
        public static IEnumerator<long> GetEnumerator(this MyCollection x) => throw null;
    }
    
    class Program
    {
        static void Main()
        {
            foreach (var l in new MyCollection())
            {
                long s = l;
            }
        
            MyCollection x1 = ["a", // error CS0029: Cannot implicitly convert type 'string' to 'long'
                               2];
        }
    }
}

namespace Ns2
{
    static class Ext
    {
        public static IEnumerator<string> GetEnumerator(this MyCollection x) => throw null;
    }
    
    class Program
    {
        static void Main()
        {
            foreach (var l in new MyCollection())
            {
                string s = l;
            }
        
            MyCollection x1 = ["a",
                               2]; // error CS0029: Cannot implicitly convert type 'int' to 'string'
        }
    }
}

namespace Ns3
{
    class Program
    {
        static void Main()
        {
            // error CS1579: foreach statement cannot operate on variables of type 'MyCollection' because 'MyCollection' does not contain a public instance or extension definition for 'GetEnumerator'
            foreach (var l in new MyCollection())
            {
            }
        
            MyCollection x1 = ["a", 2]; // error CS9188: 'MyCollection' has a CollectionBuilderAttribute but no element type.
        }
    }
}

Geçerli tasarım göz önüne alındığında, tür kendisi yineleme türünü tanımlamıyorsa, derleyici bir CollectionBuilder özniteliğinin uygulamasını güvenilir bir şekilde doğrulayamaz. yineleme türünü bilmiyorsak oluşturma yönteminin imzasının ne olması gerektiğini bilmiyoruz. yineleme türü bağlamdan geliyorsa, türün her zaman benzer bir bağlamda kullanılacağı garanti edilmez.

Params Koleksiyonları özelliği de bundan etkilenir. Bildirim noktasında bir params parametresinin öğe türünü güvenilir bir şekilde tahmin edememek garip geliyor. Geçerli teklif ayrıca oluşturma yönteminin en az paramskoleksiyon türükadar erişilebilir olmasını gerektirir. koleksiyon türüyineleme türünü kendisi tanımlamadığı sürece, bu denetimi güvenilir bir şekilde gerçekleştirmek mümkün değildir.

Unutmayın ki, derleyici için açılmış olan https://github.com/dotnet/roslyn/issues/69676 aynı sorunu gözlemlemekte ve bunu iyileştirme açısından ele almaktadır.

Teklif

özniteliğini kullanarak kendi yineleme türünü tanımlayan bir tür gerektir. Başka bir deyişle, türün IEnumarable/IEnumerable<T>uygulaması veya doğru imzaya sahip genel GetEnumerator yöntemine sahip olması gerektiği anlamına gelir (bu, tüm uzantı yöntemlerini dışlar).

Ayrıca şu anda oluşturma yöntemi "koleksiyon ifadesinin kullanıldığı yerde erişilebilir olması" gerekir. Bu, erişilebilirliğe dayalı bağlam bağımlılığının başka bir noktasıdır. Bu yöntemin amacı, kullanıcı tanımlı dönüştürme yönteminin amacına çok benzer ve genel olması gerekir. Bu nedenle, oluşturma yönteminin de genel olmasını zorunlu bırakmalıyız.

Sonuç

LDM-2024-01-08 değişikliklerle onaylandı

Yineleme türü kavramı, dönüştürmeler boyunca tutarlı bir şekilde uygulanmamaktadır

  • yapısına veya System.Collections.Generic.IEnumerable<T> şartıyla:
    • Eiöğesi için, 'e T vardır.

Bu durumda, T'ın gerekli olduğuna dair varsayım, yapısı veya sınıfı türünün yineleme türü olarak yapılmış gibi görünüyor. Ancak bu varsayım yanlıştır. Bu da çok garip bir davranışa yol açabilir. Mesela:

using System.Collections;
using System.Collections.Generic;

class MyCollection : IEnumerable<long>
{
    IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null;
    IEnumerator IEnumerable.GetEnumerator() => throw null;

    public void Add(string l) => throw null;
    
    public IEnumerator<string> GetEnumerator() => throw null; 
}

class Program
{
    static void Main()
    {
        foreach (var l in new MyCollection())
        {
            string s = l; // Iteration type is string
        }
        
        MyCollection x1 = ["a", // error CS0029: Cannot implicitly convert type 'string' to 'long'
                           2];
        MyCollection x2 = new MyCollection() { "b" };
    }
}
  • yapısına veya sınıf türüne ki System.Collections.IEnumerable uygular ve uygulamazSystem.Collections.Generic.IEnumerable<T>.

Uygulama, yineleme türünün objectolduğunu varsayar, ancak spesifikasyon bu olguyu belirtilmeden bırakır ve sadece her öğesinin herhangi bir şeye dönüştürülmesini gerektirmez. Ancak genel olarak yineleme türüobject türü gerekli değildir. Aşağıdaki örnekte gözlemlenebilir:

using System.Collections;
using System.Collections.Generic;

class MyCollection : IEnumerable
{
    public IEnumerator<string> GetEnumerator() => throw null; 
    IEnumerator IEnumerable.GetEnumerator() => throw null;
}

class Program
{
    static void Main()
    {
        foreach (var l in new MyCollection())
        {
            string s = l; // Iteration type is string
        }
    }
}

yineleme türü, Params Koleksiyonları özelliğini için temeldir. Ve bu sorun iki özellik arasında garip bir tutarsızlığa yol açar. Mesela:

using System.Collections;
using System.Collections.Generic;

class MyCollection : IEnumerable<long>
{
    IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null;
    IEnumerator IEnumerable.GetEnumerator() => throw null;

    public IEnumerator<string> GetEnumerator() => throw null; 

    public void Add(long l) => throw null; 
    public void Add(string l) => throw null; 
}

class Program
{
    static void Main()
    {
        Test("2"); // error CS0029: Cannot implicitly convert type 'string' to 'long'
        Test(["2"]); // error CS1503: Argument 1: cannot convert from 'collection expressions' to 'string'
        Test(3); // error CS1503: Argument 1: cannot convert from 'int' to 'string'
        Test([3]); // Ok

        MyCollection x1 = ["2"]; // error CS0029: Cannot implicitly convert type 'string' to 'long'
        MyCollection x2 = [3];
    }

    static void Test(params MyCollection a)
    {
    }
}
using System.Collections;
using System.Collections.Generic;

class MyCollection : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator() => throw null;

    public IEnumerator<string> GetEnumerator() => throw null; 
    public void Add(object l) => throw null;
}

class Program
{
    static void Main()
    {
        Test("2", 3); // error CS1503: Argument 2: cannot convert from 'int' to 'string'
        Test(["2", 3]); // Ok
    }

    static void Test(params MyCollection a)
    {
    }
}

Bir şekilde veya başka şekilde hizalamak muhtemelen iyi olacaktır.

Teklif

Dönüştürülebilirliğini belirlemek için, yineleme türü açısından, veya System.Collections.Generic.IEnumerable<T>'i uygulayan System.Collections.IEnumerable veya sınıf türü için, heröğesinin Eiyineleme türüne örtük dönüşüm gerektirildiğini belirtin.

Sonuç

Onaylandı LDM-2024-01-08

koleksiyon ifadesi dönüştürme, derleme için en az sayıda API'nin kullanılabilir olmasını gerektirir mi?

ile soyutlanmışdönüştürmeleri sonucu oluşturulabilir koleksiyon türü, aslında yapılamayabilir ve bu durum beklenmedik aşırı yükleme çözümleme davranışlarına yol açabilir. Mesela:

class C1
{
    public static void M1(string x)
    {
    }
    public static void M1(char[] x)
    {
    }
    
    void Test()
    {
        M1(['a', 'b']); // error CS0121: The call is ambiguous between the following methods or properties: 'C1.M1(string)' and 'C1.M1(char[])'
    }
}

Ancak, 'C1. M1(dize)' şu nedenden dolayı kullanılabilecek bir aday değildir:

error CS1729: 'string' does not contain a constructor that takes 0 arguments
error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Aşağıda, kullanıcı tanımlı bir türe ve geçerli bir adaydan bile bahsetmeyen daha güçlü bir hataya sahip başka bir örnek verilmiştir:

using System.Collections;
using System.Collections.Generic;

class C1 : IEnumerable<char>
{
    public static void M1(C1 x)
    {
    }
    public static void M1(char[] x)
    {
    }

    void Test()
    {
        M1(['a', 'b']); // error CS1061: 'C1' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'C1' could be found (are you missing a using directive or an assembly reference?)
    }

    public static implicit operator char[](C1 x) => throw null;
    IEnumerator<char> IEnumerable<char>.GetEnumerator() => throw null;
    IEnumerator IEnumerable.GetEnumerator() => throw null;
}

Durum, dönüşümleri temsilci olarak atamak için yöntem grubuyla sahip olduğumuz durumla çok benzer görünüyor. Yani dönüştürmenin var olduğu ama hatalı olduğu senaryolar vardı. Dönüştürmenin hatalı olması durumunda mevcut olmamasını sağlayarak bunu geliştirmeye karar verdik.

"Params Koleksiyonları" özelliğiyle benzer bir sorunla karşılaşacağımızı unutmayın. Oluşturulamaz koleksiyonlar için params değiştirici kullanımına izin vermemek iyi olabilir. Ancak mevcut teklifte bu denetim, dönüştürmeleri bölümü üzerine kuruludur. Aşağıda bir örnek verilmiştir:

using System.Collections;
using System.Collections.Generic;

class C1 : IEnumerable<char>
{
    public static void M1(params C1 x) // It is probably better to report an error about an invalid `params` modifier
    {
    }
    public static void M1(params ushort[] x)
    {
    }

    void Test()
    {
        M1('a', 'b'); // error CS1061: 'C1' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'C1' could be found (are you missing a using directive or an assembly reference?)
        M2('a', 'b'); // Ok
    }

    public static void M2(params ushort[] x)
    {
    }

    IEnumerator<char> IEnumerable<char>.GetEnumerator() => throw null;
    IEnumerator IEnumerable.GetEnumerator() => throw null;
}

Sorun daha önce biraz tartışılmış gibi görünüyor, bkz. https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-10-02.md#collection-expressions. O sırada, kuralların şu anda belirtildiği gibi, dize ara işleyicilerinin nasıl belirtildiğiyle tutarlı olduğu bir argüman ortaya sunuldu. İşte bir alıntı:

Özellikle, ilişkilendirilmiş dize işleyicileri başlangıçta bu şekilde belirtildi, ancak bu sorunu dikkate aldıktan sonra belirtimi düzelttik.

Bazı benzerlikler olsa da, dikkate alınması gereken önemli bir ayrım da vardır. İşte https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/improved-interpolated-strings.md#interpolated-string-handler-conversion'dan bir alıntı:

Eğer tür T, ile ilişkilendirilmişse, System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute olarak kabul edilir. bir interpolated_string_expressionT için örtük bir interpolated_string_handler_conversion veya tamamen _interpolated_string_expression_s oluşturulmuş ve yalnızca işleçleri kullanan bir + vardır.

Hedef türün, türün ilişkilendirilmiş dize işleyicisi olması için yazarın amacının güçlü bir göstergesi olan özel bir özniteliği olmalıdır. Özniteliğin varlığının tesadüf olmadığını varsaymak adildir. Buna karşılık, bir türün "numaralandırılabilir" olması, türün oluşturulabilir olması için yazarın amacının olduğu anlamına gelmez. Ancak, koleksiyon türünde özniteliğiyle belirtilen bir [CollectionBuilder(...)]varlığı, yazarın türün oluşturulabilir olmasını amaçladığının güçlü bir göstergesi olarak dikkate alınmalıdır.

Teklif

uygulayan, ancak oluşturma yöntemi bulunmayan yapı veya System.Collections.IEnumerable için,dönüştürmeleri bölümünde en azından aşağıdaki API'lerin bulunması gerekir:

  • Bağımsız değişken gerektirmeyen erişilebilir bir oluşturucu.
  • Bağımsız değişken olarak Add yineleme türü değeriyle çağrılabilen erişilebilir bir örneği veya uzantı yöntemi.

Params Collectons özelliğini amacıyla, bu tür türler, bu API'ler genel olarak bildirildiğinde ve örnek (uzantı) yöntemleri olduğunda geçerli params türleridir.

Sonuç

LDM-2024-01-10 değişikliklerle onaylandı

Tasarım toplantıları

https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-11-01.md#collection-literals https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-09.md#ambiguity-of--in-collection-expressions https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-28.md#collection-literals https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-08.md https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-01-10.md

Çalışma grubu toplantıları

https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2022-10-06.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2022-10-14.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2022-10-21.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2023-04-05.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2023-04-28.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2023-05-26.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2023-06-12.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2023-06-26.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2023-08-03.md https://github.com/dotnet/csharplang/blob/main/meetings/working-groups/collection-literals/CL-2023-08-10.md

Yaklaşan ajanda öğeleri

  • Büyük koleksiyonlar için yığın üzerinde yapılan bellek tahsisleri yığının taşmasına neden olabilir. Derleyicinin bu verileri yığın üzerinde yerleştirmek için buluşsal bir yöntemi olması gerekir mi? Bu esnekliğe izin vermek için dil belirtilmemiş olmalıdır? Şartname/uygulamanın params Span<T>için ne yaptığını takip etmeliyiz. Seçenekler şunlardır:

    • Her zaman stackalloc. İnsanlara Span konusunda dikkatli olmayı öğret. Bu, Span<T> span = [1, 2, ..s] gibi şeylerin çalışmasını sağlar ve s küçük olduğu sürece bu durum sorunsuz devam eder. Bu işlem yığını patlatabilirse, kullanıcılar her zaman bunun yerine bir dizi oluşturabilir ve ardından bunun çevresinde bir yayılma alanı elde edebilir. Bu, insanların isteyebileceklerine en uygun gibi görünüyor, ancak aşırı tehlikeyle.
    • Yalnızca öğelerin sabit sayıda olduğu durumlarda stackalloc kullanın (yani yayılan öğeler yoksa). Bu da büyük olasılıkla sabit yığın kullanımıyla işleri her zaman güvenli hale getirir ve derleyici (umarak) bu sabit arabelleği yeniden kullanabilir. Ancak, kullanıcı çalışma zamanında tamamen güvenli olduğunu bildiğinden bile [1, 2, ..s] gibi şeylerin asla mümkün olacağı anlamına gelir.
  • Aşırı yükleme çözümlemesi nasıl çalışır? Eğer bir API'de:

    public void M(T[] values);
    public void M(List<T> values);
    

    M([1, 2, 3])ile ne olacak? Büyük olasılıkla bu dönüşümler için 'iyilik' tanımlamamız gerekir.

  • En yaygın AddRange yöntemini aramak için koleksiyon başlatıcılarını genişletmeli miyiz? İnşa edilen temel tür, yayma elemanlarının potansiyel olarak daha verimli bir şekilde eklenmesini sağlamak için kullanılabilir. .CopyTo gibi şeyleri de aramak isteyebiliriz. Bu yöntemler, çevrilen kodda doğrudan listeleme yerine gereksiz bellek tahsisi/gönderimine neden olabileceğinden burada dezavantajlar olabilir.

  • Tür çıkarımı, koleksiyon değişmezlerinden ve koleksiyon değişmezlerine tür bilgilerini akıtacak şekilde güncellenmelidir. Mesela:

    void M<T>(T[] values);
    M([1, 2, 3]);
    

    Bunun çıkarım algoritmasının farkında olunabileceği bir şey olması doğal görünüyor. Bu durum, 'temel' yapılı koleksiyon türü durumları için desteklendiğinde (T[], I<T>, Span<T>,new T()), Collect(constructible_type) durumundan da çıkacaktır. Mesela:

    void M<T>(ImmutableArray<T> values);
    M([1, 2, 3]);
    

    Burada Immutable<T> bir init void Construct(T[] values) yöntemiyle oluşturulur. Bu nedenle T[] values türü, [1, 2, 3] karşıt çıkarsaması ile intiçin T çıkarsamasına yol açacak şekilde kullanılır.

  • Atama/Dizin belirsizliği.

    Bugün aşağıdakiler dizinine alınan bir ifadedir

    var v = (Expr)[1, 2, 3];
    

    Ama şöyle şeyler yapabilmek güzel olurdu:

    var v = (ImmutableArray<int>)[1, 2, 3];
    

    Burada bir mola verebilir miyiz?

  • ?[ile ilgili sözdizimsel belirsizlikler.

    nullable index access ile ?arasında boşluk olmaması gerektiğini belirtmek için [ kurallarını değiştirmek faydalı olabilir. Önemli bir değişiklik olacaktır (ancak muhtemelen küçüktür, çünkü bir boşlukla yazdığınızda VS bunları zaten birleştirir). Bunu yaparsak, x?[y]'nın x ? [y]'den farklı şekilde ayrıştırılabileceğini sağlayabiliriz.

    https://github.com/dotnet/csharplang/issues/2926'ı tercih etmek istersek benzer bir durum ortaya çıkar. Bu dünyada x?.yx ? .yile belirsizdir. Eğer ?.'a bitişmesi gerekirse, iki durumu kolayca ayırt edebiliriz.