Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
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:
-
Dizi türleri,
int[]gibi. -
Span<T>veReadOnlySpan<T>. -
koleksiyon başlatıcılarını destekleyen türler(
List<T>gibi).
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 öncenew[]veya{ ... }gerektiren diziler. - Spans,
stackallocve 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ğuTgibi 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_expressionaşağıdaki bölümlerde "literal" olarak adlandırılır.expression_elementörnekleri genelliklee1,e_nvb. olarak adlandırılır.spread_elementörnekleri genellikle..s1,..s_nvb. olarak adlandırılır.span türü
Span<T>veyaReadOnlySpan<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_elementiçermeyen sabitler. - İçerisinde
spread_elementiçermeyen sabitler. - Rastgele sıralanabilen herhangi bir öğe türüne sahip sabit değerler.
- Boş sabit ifadeler
..s_nyineleme türü, bir üzerinde yinelenen ifade olaraks_nkullanılmış gibi belirlenenforeach_statementyineleme değişkeninin türüdür.__nameile başlayan değişkenler, yalnızca bir kez değerlendirilecek şekilde bir konumda depolanannamedeğerlendirmesinin sonuçlarını göstermek için kullanılır. Örneğin__e1,e1değerlendirmesidir.List<T>,IEnumerable<T>vb.System.Collections.Genericad 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 birnew int[] { 1, 2, 3 }ifadesine çevirebilir ve böylelikle__indexihtiyacı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.
- Örneğin, bir uygulama
'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
stackallocmekanizması 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
Countdeğerinin numaralandırıldığında öğe sayısıyla aynı değeri üreteceği varsayılır. -
System.Collections.Genericad 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.
- Bir koleksiyondaki
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ü,Tolur -
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 türüdür
-
yapı veya sınıf türü uygulayan
System.Collections.IEnumerabletü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ü
Addbir ö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 türüT
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ᵢ'denT'e örtük bir dönüştürme vardır. -
Eᵢ, bir yayma öğesiyse,..Sᵢ'den 'yeSᵢ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üne
T?, 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 'danT'ya örtük null yapılabilir dönüştürmeT?takip eder.Tbaşvuru türüne bir oluşturma yöntemi,Tile ilişkilendirilmiş veU'dan 'a birUolan bir türTdöndüren. Dönüştürme, koleksiyon ifadesine dönüştürme'denU'ye, ardından 'denU'yaTşeklindedir.Arabirim türüne
Iile ilişkilendirilmiş bir tür döndüren birIveVV. 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, oluş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 veAddç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çerliAddö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
CopyTodizini bağımsız değişken olarak spread öğesi ifadesinde geçerli birintörneği veya uzantı yöntemi çağrılır.
-
- Öğe ,bir ifade öğesiyse, bağımsız değişken olarak
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ı ileintkoleksiyon ö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 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.
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 olarakintü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];
bvecöğeleri sayılabilir ise, derleyiciadeğerlendirilinceye kadarbvecöğelerinin eklenmesini geciktirebilir, böylece sonuçta elde edilen dizinin beklenen uzunlukta tahsis edilmesine olanak tanıyabilir. Bundan sonra, derleyicicdeğerlendirmeden öncedöğ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 = []; // illegalBoş 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,
byanlış 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. Örneğin:
// 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
System.ReadOnlySpan<T>,Tve koleksiyon ifadesi yalnızca sabit değerler içeriyorsa, koleksiyon ifadesinin güvenli bağlamıbool.Hedef tür, yayılma türü
System.Span<T>veyaSystem.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
Ebir türeTyapılır:
11.6.3.7 Çıkış türü çıkarımları
Bir çıkış türü çıkarımı, aşağıdaki şekilde bir ifadeden bir türe
ETyapılır:
Eöğeleri ile birEᵢveTTürü öğe türüne sahip bir türse veyaTₑnull atanabilir birTveT0?sahipse, herT0için:
- Eğer
Eᵢ, birifade öğesiyse, o zaman 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₁>, veT₂System.Span<E₂>, veE₁'danE₂'a örtük bir dönüşüm vardırT₁,System.ReadOnlySpan<E₁>veyaSystem.Span<E₁>'tür veT₂'dan 'a örtük dönüştürme vardır.E₂iseE₁öğe türüne sahip birE₂'dur.T₁bir span_typedeğildir veT₂bir span_typedeğildir veT₁'denT₂'a örtük bir dönüştürme vardır.Ebir koleksiyon ifadesi değildir ve aşağıdakilerden biri geçerlidir:Ebir 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:
- Gerekli arabirimleri uygulayan mevcut bir türü kullanın.
- 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.
-
true(uygulandıysa) ve nongenericICollection<T>.IsReadOnlyveIList.IsReadOnlyiçin sorgulandığında değerIList.IsFixedSizedö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. - 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ü:
- 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ü
Tolarak belirlenmiştir.TbirT1[]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 elementsUygulamanın diziyi doldurmak için diğer araçları kullanmasına izin verilir. Örneğin,
.CopyTo()gibi verimli toplu kopyalama yöntemlerini kullanma.TbazıSpan<T1>ise, bu durumda literal ifade yukarıdakiyle aynı şekilde çevrilir, ancak__resultbaşlatma şu şekilde çevrilir:Span<T1> __result = new T1[__len]; // same assignments as the array translationÇeviri,
stackalloc T1[]yerine span-safety korunursanew T1[]veya satır içi dizi kullanabilir.TbazıReadOnlySpan<T1>ise,Span<T1>durumu için olduğu gibi 'literal' aynı şekilde çevrilir; ancak nihai sonuç,Span<T1>'ünaracılığıyla'tenReadOnlySpan<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 birT1'ı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
TilgiliC<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.Tkoleksiyon 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 elementsNot: 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 elementsBu, 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
Tdeğişmez değeri için hedef türü verilen:Tkoleksiyon 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 elementsBu, 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.
TbirT1[]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.Tbir türse, bir uygulama yukarıdakiT[]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..Childrenatanamı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_elementrange_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ıç dizini0..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.
- Kullanıcıların bir aralık istemesi durumunda,
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_expressionveattributesarası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_expressionileconditional_expressionvenull_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_operationyorumunu 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
halkasö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,
stackallockullanı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.
stackallocarabelleğ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, birIEnumerable<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ü olanList<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>veyaIReadOnlyList<T>etiketlerini hedefleme. Hedef değiştirilebilir olduğunda (Array.Empty<T>veyaICollection<T>)IList<T>kullanmamalıdır.En yaygın
AddRangeyö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..CopyTogibi ş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_elementifadesini 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
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 şartıyla:
- öğesi için,
Ei'e 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. Örneğin:
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.IEnumerableuygular 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. Örneğin:
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.InterpolatedStringHandlerAttributeolarak kabul edilir. bir interpolated_string_expressionTiç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
Addyineleme 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ç
C# feature specifications