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,
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ğuT
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.CreateBuilder
gibi) 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 genelliklee1
,e_n
vb. olarak adlandırılır.spread_element
örnekleri genellikle..s1
,..s_n
vb. 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_element
içermeyen sabitler. - İçlerinde
spread_element
olmayan sabit değerler. - Rastgele sıralanabilen herhangi bir öğe türüne sahip sabit değerler.
- Boş sabit ifadeler
..s_n
yineleme türü, bir üzerinde yinelenen ifade olaraks_n
kullanılmış gibi belirlenenforeach_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 depolananname
değerlendirmesinin sonuçlarını göstermek için kullanılır. Örneğin__e1
,e1
değ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 birnew 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.
- Ö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
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.Add
ile 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ü,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ᵢ
'denT
'e örtük bir dönüştürme vardır. -
Eᵢ
,..Sᵢ
bir yayma öğesiyse, 'denSᵢ
'yeT
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.T
başvuru türüne bir oluşturma yöntemi,T
ile ilişkilendirilmiş veU
'dan 'a birU
olan bir türT
döndüren. Dönüştürme, koleksiyon ifadesine dönüştürme'denU
'ye, ardından 'denU
'yaT
şeklindedir.Arabirim türüne
I
ile ilişkilendirilmiş bir tür döndüren birV
veV
I
. 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 struct
veya interface
olarak uygulanabilir.
Öznitelik bir temel class
veya abstract class
'e uygulanabilir, ancak devralınmamıştır.
oluşturucu türü genel olmayan bir class
veya struct
olmalıdır.
İlk olarak, CM
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
static
olmalı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 scoped
ise, 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
, Count
ve 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ıIDisposable
uygularsa,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 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ı ileint
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ıIDisposable
uygularsa,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];
b
vec
öğeleri sayılabilir ise, derleyicia
değerlendirilinceye kadarb
vec
öğelerinin eklenmesini geciktirebilir, böylece sonuçta elde edilen dizinin beklenen uzunlukta tahsis edilmesine olanak tanıyabilir. Bundan sonra, derleyicic
değ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 = []; // 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>
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
E
bir türeT
yapılır:
E
öğeleriEᵢ
ile bir koleksiyon ifadesiyse veT
Türü öğe türüne sahip bir türse veyaT
null atanabilir bir değer türüyse veT0
öğe türüne sahipse, herEᵢ
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
E
T
yapılır:
E
öğeleriyleEᵢ
bir koleksiyon ifadesiyse veT
T
öğe türüne sahip bir türseT0?
değer türüyse veT0
Tₑ
öğe türüne sahipse, herEᵢ
için :
- Eğer
Eᵢ
, birifade öğesiyse, o zamanTₑ
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 bir array_or_array_interface'dur.T₁
bir span_typedeğildir veT₂
bir span_typedeğildir veT₁
'denT₂
'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 veE
tam olarakT₂
ile eşleşmez.E
, hemT₁
hem deT₂
ile veya hiçbirisiyle tam olarak eşleşir veT₁
, 'den daha iyi bir dönüştürme hedefi olarakT₂
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:
- 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>.IsReadOnly
veIList.IsReadOnly
için sorgulandığında değerIList.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. - Değerin bir mutasyon yöntemine yapılan herhangi bir çağrıda (
IList<T>.Add
gibi) 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ü
T
olarak belirlenmiştir.T
birT1[]
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 korunursastackalloc 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'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
T
ilgiliC<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 capacity
tek 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
capacity
olması 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
birT1[]
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ı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..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_expression
kullanan kod için birden çok geçerli söz dizimi yorumu bulunmaktadır.spread_element
range_expression
ile 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_expression
veattributes
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
ileconditional_expression
venull_conditional_operations
arası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_expressions
aracı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
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,
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
, 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
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_element
tü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 params
koleksiyon 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
System.Collections.Generic.IEnumerable<T>
şartıyla:
Ei
öğesi için, 'eT
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 object
olduğ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 Ei
yineleme 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 ves
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.
- Her zaman stackalloc. İnsanlara Span konusunda dikkatli olmayı öğret. Bu,
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>
birinit void Construct(T[] values)
yöntemiyle oluşturulur. Bu nedenleT[] values
türü,[1, 2, 3]
karşıt çıkarsaması ileint
içinT
çı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ınx ? [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?.y
x ? .y
ile belirsizdir. Eğer?.
'a bitişmesi gerekirse, iki durumu kolayca ayırt edebiliriz.
C# feature specifications