Aracılığıyla paylaş


params Collections

Uyarı

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

Özellik belirtimi ile tamamlanan uygulama arasında bazı tutarsızlıklar olabilir. Bu farklılıklar, 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çinbelirtimleri makalesinde bulabilirsiniz.

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

Özet

C# 12 dilinde yalnızca dizilerin ötesinde koleksiyon türlerinin örneklerini oluşturmak için destek eklendi. Bkz. koleksiyon ifadeleri. Bu teklif, desteği bu tür tüm koleksiyon türlerine genişletir params .

Motivasyon

Dizi params parametresi, bağımsız değişkenlerin rastgele uzunluk listesini alan bir yöntemi çağırmak için kullanışlı bir yol sağlar. Bugün params parametresi bir dizi türü olmalıdır. Ancak, bir geliştiricinin diğer koleksiyon türlerini alan API'leri çağırırken aynı kolaylıklara sahip olması yararlı olabilir. Örneğin, bir ImmutableArray<T>, ReadOnlySpan<T>veya düz IEnumerable. Özellikle derleyicinin koleksiyonu (ImmutableArray<T>, ReadOnlySpan<T>vb.) oluşturma amacıyla örtük dizi ayırmasını önleyebileceği durumlarda.

Günümüzde, bir API'nin koleksiyon türünü aldığı durumlarda geliştiriciler genellikle dizi alan, params hedef koleksiyonu oluşturan ve bu koleksiyonla özgün aşırı yüklemeyi çağıran bir aşırı yükleme ekler, bu nedenle API tüketicilerinin kolaylık sağlamak için ek bir dizi ayırma işlemine sahip olması gerekir.

Bir diğer motivasyon, yeniden derleme yoluyla mevcut kaynak kodunu değiştirerek, bir params span aşırı yüklemesi ekleyebilmek ve bu aşırı yüklemenin dizi versiyonunun önüne geçmesini sağlamaktır.

Ayrıntılı tasarım

Yöntem parametreleri

Yöntem parametreleri bölümü aşağıdaki gibi ayarlanır.

formal_parameter_list
    : fixed_parameters
-    | fixed_parameters ',' parameter_array
+    | fixed_parameters ',' parameter_collection
-    | parameter_array
+    | parameter_collection
    ;

-parameter_array
+parameter_collection
-    : attributes? 'params' array_type identifier
+    : attributes? 'params' 'scoped'? type identifier
    ;

Bir parametre_kümesi, isteğe bağlı bir öznitelik kümesi, bir params, isteğe bağlı bir scoped, bir tür ve bir tanımlayıcı içerir. Parametre koleksiyonu, verilen adla verilen türün tek bir parametresini bildirir. Parametre koleksiyonunun türü , bir koleksiyon ifadesi için aşağıdaki geçerli hedef türlerden biri olmalıdır (bkz https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#conversions: ):

  • tek boyutlu dizi türüT[], bu durumda öğe türü, T olur
  • Bir span türü
    • System.Span<T>
    • System.ReadOnlySpan<T>
      bu durumda öğe türüT
  • En az bildirimde bulunan üye kadar erişilebilir olan ve bu belirlemeden kaynaklanan ilgili öğe türüne sahip ek bağımsız değişkenler olmadan çağrılabilen uygun create yöntemine sahip bir tür
  • yapı veya sınıf türü uygulayan System.Collections.IEnumerable
    • türü, bağımsız değişken olmadan çağrılabilen bir oluşturucuya sahiptir ve oluşturucu, en az bildirimi yapan üye kadar erişilebilir durumdadır.

    • türü, aşağıdaki durumlarda bir örnek (uzantı değil) yöntemine Add sahiptir:

      • Yöntem, tek bir değer bağımsız değişkeniyle çağrılabilir.
      • Yöntem genelse, tür argümanları argümandan çıkarılabilir.
      • yöntemi en az bildirimde bulunan üye kadar erişilebilir.

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

  • 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

Yöntem çağırmada, parametre koleksiyonu belirtilen parametre türünün tek bir bağımsız değişkeninin belirtilmesine izin verir veya koleksiyonun öğe türünün sıfır veya daha fazla bağımsız değişkeninin belirtilmesine izin verir. Parametre koleksiyonları, Parametre koleksiyonları bölümünde daha ayrıntılı olarak açıklanmıştır.

İsteğe bağlı bir parametreden sonra bir parameter_collection oluşabilir, ancak varsayılan değere sahip olamaz; bunun yerine bir parameter_collection için bağımsız değişkenlerin atılması boş bir koleksiyonun oluşturulmasına neden olur.

Parametre koleksiyonları

Parametre dizileri bölümü aşağıdaki gibi yeniden adlandırılır ve ayarlanır.

Değiştirici ile bildirilen parametre params , parametre koleksiyonudur. Resmi parametre listesi bir parametre koleksiyonu içeriyorsa, listedeki son parametre ve Yöntem parametreleri bölümünde belirtilen türde olmalıdır.

Not: Değiştiriciyi params , inveya outdeğiştiricileriyle refbirleştirmek mümkün değildir. dipnot

Parametre koleksiyonu, yöntem çağırmada bağımsız değişkenlerin iki yoldan biriyle belirtilmesine izin verir:

  • Parametre koleksiyonu için verilen bağımsız değişken, parametre koleksiyonu türüne örtük olarak dönüştürülebilen tek bir ifade olabilir. Bu durumda, parametre koleksiyonu tam olarak bir değer parametresi gibi davranır.
  • Alternatif olarak, çağırma parametre koleksiyonu için sıfır veya daha fazla bağımsız değişken belirtebilir; burada her bağımsız değişken, parametre koleksiyonunun öğe türüne örtük olarak dönüştürülebilen bir ifadedir. Bu durumda, çağırma, bağımsız değişkenler bir koleksiyon ifadesinde aynı sırada ifade öğeleri olarak kullanılmış gibi Koleksiyon ifadelerinde belirtilen kurallara göre parametre koleksiyonu türünün bir örneğini oluşturur ve yeni oluşturulan koleksiyon örneğini gerçek bağımsız değişken olarak kullanır. Koleksiyon örneği oluşturulurken özgün çevrilmemiş bağımsız değişkenler kullanılır.

Bir çağrıda değişken sayıda bağımsız değişkene izin vermek dışında, parametre koleksiyonu tam olarak aynı türde bir değer parametresine eşdeğerdir.

Aşırı yükleme çözümlemesi gerçekleştirirken, parametre koleksiyonuna sahip bir yöntem normal biçiminde veya genişletilmiş biçiminde uygulanabilir. Bir yöntemin genişletilmiş biçimi yalnızca yöntemin normal biçimi geçerli değilse ve yalnızca genişletilmiş formla aynı imzaya sahip geçerli bir yöntem aynı türde önceden bildirilmemişse kullanılabilir.

Parametre koleksiyonunun kendisi ve parametre koleksiyonunun öğesi olarak aynı anda kullanılabildiğinde, tek bir parametre koleksiyonu bağımsız değişkeniyle yöntemin normal formu ile genişletilmiş biçimi arasında olası bir belirsizlik ortaya çıkar. Ancak, gerekirse bir atama eklenerek veya koleksiyon ifadesi kullanılarak çözümlenebildiği için belirsizlik herhangi bir sorun sunmaz.

İmzalar ve aşırı yükleme

İmzalar ve aşırı yüklemede params değiştirici ile ilgili tüm kurallar olduğu gibi kalır.

Geçerli işlev üyesi

Geçerli işlev üyesi bölümü aşağıdaki gibi ayarlanır.

Parametre koleksiyonu içeren bir işlev üyesi normal biçiminde uygulanamazsa, işlev üyesi bunun yerine genişletilmiş biçiminde uygulanabilir:

  • Parametre koleksiyonu bir dizi değilse, genişletilmiş form C# 12 ve altındaki dil sürümleri için geçerli değildir.
  • Genişletilmiş form, işlev üyesi bildirimindeki parametre koleksiyonu, parametre koleksiyonunun öğe türünün sıfır veya daha fazla değer parametresiyle değiştirilerek oluşturulur; böylece bağımsız değişken listesindeki A bağımsız değişken sayısı toplam parametre sayısıyla eşleşir. A, işlev üyesi bildirimindeki sabit parametre sayısından daha az bağımsız değişkene sahipse, işlev üyesinin genişletilmiş biçimi oluşturulamaz ve bu nedenle geçerli değildir.
  • Aksi takdirde, Aiçindeki her bağımsız değişken için aşağıdakilerden biri doğruysa genişletilmiş form geçerlidir:
    • bağımsız değişkenin parametre geçirme modu, karşılık gelen parametrenin parametre geçirme moduyla aynıdır ve
      • sabit bir değer parametresi veya genişletme yoluyla oluşturulan bir değer parametresi için, bağımsız değişken ifadesinden ilgili parametrenin türüne otomatik bir dönüşüm vardır veya
      • , inoutveya ref parametresi için bağımsız değişken ifadesinin türü, karşılık gelen parametrenin türüyle aynıdır.
    • Bağımsız değişkenin parametre geçiş modu değerken ve karşılık gelen parametrenin parametre geçiş modu girişken, bağımsız değişken ifadesinden karşılık gelen parametrenin türüne doğru bir örtük dönüşüm mevcuttur.

Daha iyi işlev üyesi

İşlev üyesi daha iyi bölümü aşağıdaki gibi ayarlanır.

Bağımsız değişken ifadesi kümesine sahip bir bağımsız değişken listesi A verildiğinde ve parametre türleri {E₁, E₂, ..., Eᵥ} ve Mᵥolan iki geçerli işlev üyesi Mₓ ve {P₁, P₂, ..., Pᵥ} olduğunda, {Q₁, Q₂, ..., Qᵥ}'nın Mᵥ'den daha iyi bir işlev üyesi olarak tanımlanması, şuna bağlıdır.

  • her bağımsız değişken için, Eᵥ'dan Qᵥ'e olan örtük dönüşüm, Eᵥ'den Pᵥ'e olan örtük dönüşümden daha iyi değildir ve
  • en az bir argüman için, Eᵥ'dan Pᵥ'e dönüşüm, Eᵥ'den Qᵥ'e dönüşümden daha iyidir.

parametre türü dizileri {P₁, P₂, ..., Pᵥ} ve {Q₁, Q₂, ..., Qᵥ} eşdeğerse (yani, her Pᵢ'nin karşılık gelen Qᵢ'e bir kimlik dönüşümü varsa), daha iyi işlev üyesini belirlemek için sırasıyla aşağıdaki bağlama kırma kuralları uygulanır.

  • Mᵢ genel olmayan bir yöntemse ve Mₑ genel bir yöntemse, MᵢMₑ'den daha iyidir.
  • Aksi takdirde, Mᵢ normal biçiminde uygulanabiliyorsa ve Mₑ bir params koleksiyonuna sahipse ve yalnızca genişletilmiş biçiminde uygulanabiliyorsa, o zaman Mᵢ, Mₑ'den daha iyidir.
  • Aksi takdirde, her iki yöntemin de param koleksiyonları varsa ve yalnızca genişletilmiş formlarında uygulanıyorsa ve params koleksiyonunun Mᵢ params koleksiyonundan daha az öğesi varsa, Mₑ değerinden MᵢMₑdaha iyidir.
  • Aksi takdirde, MᵥMₓ'den daha belirli parametre türlerine sahipse, MᵥMₓ'den daha iyidir. {R1, R2, ..., Rn} ve {S1, S2, ..., Sn}, Mᵥ ve Mₓ'nin belirtilmemiş ve genişletilmemiş parametre türlerini temsil eder. Mᵥparametre türleri, her parametre için MₓRx'den daha az özel değilse Sx'den daha belirgindir ve en az bir parametre için RxSx'den daha belirgindir:
    • Tür parametresi, tür olmayan bir parametreden daha az özeldir.
    • Özyinelemeli bir şekilde, en az bir tür bağımsız değişkeni daha özelse ve hiçbir tür bağımsız değişkeni diğerinde karşılık gelen bağımsız değişkeninden daha az özel olmadığı sürece, bir yapılandırılmış tür, aynı sayıda tür bağımsız değişkeni olan bir başka yapılandırılmış türe göre daha özeldir.
    • Birincinin öğe türü ikincinin öğe türünden daha özelse, dizi türü başka bir dizi türünden (aynı sayıda boyuta sahip) daha belirgindir.
  • Aksi takdirde, bir üye kaldırılmayan bir operatörse ve diğeri kaldırılmış bir operatörse, kaldırılmayan operatör daha iyidir.
  • İşlev üyesinin hiçbiri daha iyi bulunamadıysa ve Mᵥ tüm parametreleri karşılık gelen bir bağımsız değişkene sahipse, varsayılan bağımsız değişkenlerin Mₓ'de en az bir isteğe bağlı parametreyle değiştirilmesi gerekiyorsa, MᵥMₓ'den daha iyidir.
  • En az bir parametre için Mᵥdaha iyi parametre geçirme seçeneği (§12.6.4.4) Mₓ'daki ilgili parametreden daha iyiyse ve Mₓ'daki parametrelerin hiçbiri Mᵥ'den daha iyi parametre geçirme seçeneğini kullanmıyorsa MᵥMₓ'den iyidir.
  • Aksi takdirde, her iki yöntemin de parametre koleksiyonları varsa ve yalnızca genişletilmiş formlarında uygulanabiliyorsa, ve aynı bağımsız değişken kümesi her iki yöntemin de koleksiyon öğelerine karşılık geliyorsa, Mᵢ, Mₑ'den daha iyidir ve aşağıdaki koşullardan biri geçerliyse (bu, https://github.com/dotnet/csharplang/blob/main/proposals/csharp-13.0/collection-expressions-better-conversion.md'ye karşılık gelir):
    • her iki param koleksiyonu da span_typedeğil ve params koleksiyonundan params koleksiyonuna Mᵢ örtük dönüştürme varMₑ
    • Mᵢ'nin params koleksiyonu System.ReadOnlySpan<Eᵢ>'dir ve Mₑ'nin params koleksiyonu System.Span<Eₑ>'dür; ayrıca Eᵢ'den Eₑ'e bir kimlik dönüştürmesi vardır
    • Mᵢ
  • Aksi takdirde, hiçbir işlev üyesi daha iyi değildir.

Yeni eşitlik bozma kuralının listenin sonuna yerleştirilmesinin nedeni son alt maddedir

  • her iki param koleksiyonu da span_typedeğil ve params koleksiyonundan params koleksiyonuna Mᵢ örtük dönüştürme varMₑ

diziler için geçerlidir ve bu nedenle, daha önce tie-break gerçekleştirmek mevcut senaryolar için bir davranış değişikliğine neden olur.

Örneğin:

class Program
{
    static void Main()
    {
        Test(1);
    }

    static void Test(in int x, params C2[] y) {} // There is an implicit conversion from `C2[]` to `C1[]`
    static void Test(int x, params C1[] y) {} // Better candidate because of "better parameter-passing choice"
}

class C1 {}
class C2 : C1 {}

Önceki bağlama kesme kurallarından herhangi biri uygulanırsa ("daha iyi bağımsız değişken dönüştürmeleri" kuralı dahil), bunun yerine açık bir koleksiyon ifadesi bağımsız değişken olarak kullanıldığında oluşan durumla karşılaştırıldığında aşırı yükleme çözümleme sonucu farklı olabilir.

Örneğin:

class Program
{
    static void Test1()
    {
        M1(['1', '2', '3']); // IEnumerable<char> overload is used because `char` is an exact match
        M1('1', '2', '3');   // IEnumerable<char> overload is used because `char` is an exact match
    }

    static void M1(params IEnumerable<char> value) {}
    static void M1(params System.ReadOnlySpan<MyChar> value) {}

    class MyChar
    {
        private readonly int _i;
        public MyChar(int i) { _i = i; }
        public static implicit operator MyChar(int i) => new MyChar(i);
        public static implicit operator char(MyChar c) => (char)c._i;
    }

    static void Test2()
    {
        M2([1]); // Span overload is used
        M2(1);   // Array overload is used, not generic
    }

    static void M2<T>(params System.Span<T> y){}
    static void M2(params int[] y){}

    static void Test3()
    {
        M3("3", ["4"]); // Ambiguity, better-ness of argument conversions goes in opposite directions.
        M3("3", "4");   // Ambiguity, better-ness of argument conversions goes in opposite directions.
                        // Since parameter types are different ("object, string" vs. "string, object"), tie-breaking rules do not apply
    }

    static void M3(object x, params string[] y) {}
    static void M3(string x, params Span<object> y) {}
}

Ancak, birincil endişemiz aşırı yüklemelerin yalnızca params koleksiyon türüne göre farklılık gösterdiği ancak koleksiyon türlerinin aynı öğe türüne sahip olduğu senaryolardır. Davranış, bu durumlar için açık koleksiyon ifadeleriyle tutarlı olmalıdır.

"Aynı bağımsız değişken kümesi her iki yöntemin de koleksiyon öğelerine karşılık geliyorsa" koşulu aşağıdaki gibi senaryolar için önemlidir:

class Program
{
    static void Main()
    {
        Test(x: 1, y: 2); // Ambiguous
    }

    static void Test(int x, params System.ReadOnlySpan<int> y) {}
    static void Test(int y, params System.Span<int> x) {}
}

Farklı öğelerden oluşturulan koleksiyonları "karşılaştırmak" mantıklı gelmiyor.

Bu bölüm LDM'de gözden geçirildi ve onaylandı.

Bu kuralların bir etkisi, farklı öğe türleri kullanıma sunulduğunda params , boş bir bağımsız değişken listesiyle çağrıldığında bunların belirsiz olmasıdır. Örneğin:

class Program
{
    static void Main()
    {
        // Old scenarios
        C.M1(); // Ambiguous since params arrays were introduced
        C.M1([]); // Ambiguous since params arrays were introduced

        // New scenarios
        C.M2(); // Ambiguous in C# 13
        C.M2([]); // Ambiguous in C# 13
        C.M3(); // Ambiguous in C# 13
        C.M3([]); // Ambiguous in C# 13
    }

    public static void M1(params int[] a) {
    }
    
    public static void M1(params int?[] a) {
    }
    
    public static void M2(params ReadOnlySpan<int> a) {
    }
    
    public static void M2(params Span<int?> a) {
    }
    
    public static void M3(params ReadOnlySpan<int> a) {
    }
    
    public static void M3(params ReadOnlySpan<int?> a) {
    }
}

Öğe türünü diğer her şeye göre öncelik sırasına aldığımıza göre, bu mantıklı görünür; kullanıcının bu senaryoda tercih int?int edip etmeyeceğini dile söyleyecek bir şey yoktur.

Dinamik Bağlama

Dizi dışı parametreler koleksiyonlarını kullanan genişletilmiş aday formları, geçerli C# çalışma zamanı bağlayıcısı tarafından geçerli aday olarak kabul edilmez.

primary_expression derleme zamanı türüne dynamicsahip değilse, yöntem çağrısı , §12.6.5 Dinamik üye çağırmasının derleme zamanı denetiminde açıklandığı gibi sınırlı bir derleme zamanı denetiminden geçer.

Testi yalnızca tek bir aday geçerse, aşağıdaki koşulların tümü karşılandığında adayın çağrılması statik olarak bağlanır:

  • aday yerel bir işlevdir
  • aday ya genel değil ya da tür bağımsız değişkenleri açıkça belirtilmiş;
  • derleme zamanında çözümlenemeyen adayın normal ve genişletilmiş biçimleri arasında bir belirsizlik yoktur.

Aksi takdirde, invocation_expression dinamik olarak bağlıdır.

Yukarıdaki testi yalnızca tek bir aday geçtiyse:

  • bu aday yerel bir işlevse derleme zamanı hatası oluşur;
  • bu aday yalnızca dizi dışı params koleksiyonlarını kullanan genişletilmiş biçimde uygulanabilirse, derleme zamanı hatası oluşur.

Ayrıca bugün yerel işlevleri etkileyen spesifikasyon ihlalini geri almayı/düzeltmeyi de düşünmeliyiz, bkz https://github.com/dotnet/roslyn/issues/71399.

LDM , bu belirtim ihlalini düzeltmek istediğimizi doğruladı.

İfade ağaçları

Koleksiyon ifadeleri, ifade ağaçlarında desteklenmez. Benzer şekilde, dizi dışı params koleksiyonlarının genişletilmiş formları ifade ağaçlarında desteklenmeyecektir. Derleyicinin, dizi dışı parametre koleksiyonlarının genişletilmiş formlarını kullanarak API'lerin kullanımını önlemek amacıyla ifade ağaçları için lambda bağlama şeklini değiştirmeyeceğiz.

Önemsiz olmayan senaryolarda dizi dışı koleksiyonlarla değerlendirme sırası

Bu bölüm LDM'de gözden geçirildi ve onaylandı. Dizi olaylarının diğer koleksiyonlardan sapmasına rağmen, resmi dil belirtiminin diziler için farklı kurallar belirtmesi gerekmez. Sapmalar yalnızca bir uygulama yapıtı olarak ele alınabilir. Aynı zamanda diziler etrafında var olan davranışı değiştirmek niyetinde değiliz.

Adlandırılmış bağımsız değişkenler

Sözcük temelli olarak önceki bağımsız değişken değerlendirildikten sonra ancak sözcük temelli olarak aşağıdaki bağımsız değişken değerlendirilmeden önce bir koleksiyon örneği oluşturulur ve doldurulur.

Örneğin:

class Program
{
    static void Main()
    {
        Test(b: GetB(), c: GetC(), a: GetA());
    }

    static void Test(int a, int b, params MyCollection c) {}

    static int GetA() => 0;
    static int GetB() => 0;
    static int GetC() => 0;
}

Değerlendirme sırası aşağıdaki gibidir:

  1. GetB çağrılır
  2. MyCollection oluşturulur ve doldurulur, GetC işlemde çağrılır
  3. GetA çağrılır
  4. Test çağrılır

Params dizi örneğinde, tüm bağımsız değişkenler sözcük sırasına göre değerlendirildikten sonra, hedef yöntem çağrılmadan hemen önce dizinin oluşturulduğunu unutmayın.

Bileşik atama

Bir koleksiyon örneği, söz dizimi açısından önceki dizin değerlendirildikten sonra, ancak söz dizimine göre takip eden dizin değerlendirilmeden önce oluşturulup doldurulur. Örnek, hedef dizin oluşturucunun alıcı ve ayarlayıcısını çağırmak için kullanılır.

Örneğin:

class Program
{
    static void Test(Program p)
    {
        p[GetA(), GetC()]++;
    }

    int this[int a, params MyCollection c] { get => 0; set {} }

    static int GetA() => 0;
    static int GetC() => 0;
}

Değerlendirme sırası aşağıdaki gibidir:

  1. GetA çağrılır ve önbelleğe alınır
  2. MyCollection oluşturulur, doldurulur ve önbelleğe alınır, GetC işlemde çağrılır
  3. Dizin oluşturucu'nun alıcısı dizinler için önbelleğe alınmış değerlerle çağrılır
  4. Sonuç artırıldı
  5. Dizin oluşturucunun ayarlayıcısı, dizinler için önbelleğe alınmış değerlerle ve artışın sonucuyla çağrılır

Boş koleksiyon içeren bir örnek:

class Program
{
    static void Test(Program p)
    {
        p[GetA()]++;
    }

    int this[int a, params MyCollection c] { get => 0; set {} }

    static int GetA() => 0;
}

Değerlendirme sırası aşağıdaki gibidir:

  1. GetA çağrılır ve önbelleğe alınır
  2. Boş MyCollection bir öğe oluşturulur ve önbelleğe alınır
  3. Dizin oluşturucu'nun alıcısı dizinler için önbelleğe alınmış değerlerle çağrılır
  4. Sonuç artırıldı
  5. Dizin oluşturucunun ayarlayıcısı, dizinler için önbelleğe alınmış değerlerle ve artışın sonucuyla çağrılır

Nesne Başlatıcı

Bir koleksiyon örneği, söz dizimi açısından önceki dizin değerlendirildikten sonra, ancak söz dizimine göre takip eden dizin değerlendirilmeden önce oluşturulup doldurulur. Örnek, gerekli olduğu takdirde dizin oluşturucunun alıcısını defalarca çağırmak için kullanılır.

Örneğin:

class C1
{
    public int F1;
    public int F2;
}

class Program
{
    static void Test()
    {
        _ = new Program() { [GetA(), GetC()] = { F1 = GetF1(), F2 = GetF2() } };
    }

    C1 this[int a, params MyCollection c] => new C1();

    static int GetA() => 0;
    static int GetC() => 0;
    static int GetF1() => 0;
    static int GetF2() => 0;
}

Değerlendirme sırası aşağıdaki gibidir:

  1. GetA çağrılır ve önbelleğe alınır
  2. MyCollection oluşturulur, doldurulur ve önbelleğe alınır, GetC işlemde çağrılır
  3. Dizin oluşturucu'nun alıcısı dizinler için önbelleğe alınmış değerlerle çağrılır
  4. GetF1 değerlendirilir ve önceki adımda döndürülen F1'nin C1 alanına atanır.
  5. Dizin oluşturucu'nun alıcısı dizinler için önbelleğe alınmış değerlerle çağrılır
  6. GetF2 değerlendirilir ve önceki adımda döndürülen F2'nin C1 alanına atanır.

Params dizi örneğinde, öğeleri değerlendirilir ve önbelleğe alınır, ancak dizin oluşturucunun alıcısının her çağrısı için dizinin yeni bir örneği (içinde aynı değerler bulunur) kullanılır. Yukarıdaki örnekte değerlendirme sırası aşağıdaki gibidir:

  1. GetA çağrılır ve önbelleğe alınır
  2. GetC çağrılır ve önbelleğe alınır
  3. Dizin oluşturucunun alıcısı, önbelleğe alınmış GetA ile çağrılır ve önbelleğe alınmış GetC ile doldurulan yeni dizi oluşturulur.
  4. GetF1 değerlendirilir ve önceki adımda döndürülen F1'nin C1 alanına atanır.
  5. Dizin oluşturucunun alıcısı, önbelleğe alınmış GetA ile çağrılır ve önbelleğe alınmış GetC ile doldurulan yeni dizi oluşturulur.
  6. GetF2 değerlendirilir ve önceki adımda döndürülen F2'nin C1 alanına atanır.

Boş koleksiyon içeren bir örnek:

class C1
{
    public int F1;
    public int F2;
}

class Program
{
    static void Test()
    {
        _ = new Program() { [GetA()] = { F1 = GetF1(), F2 = GetF2() } };
    }

    C1 this[int a, params MyCollection c] => new C1();

    static int GetA() => 0;
    static int GetF1() => 0;
    static int GetF2() => 0;
}

Değerlendirme sırası aşağıdaki gibidir:

  1. GetA çağrılır ve önbelleğe alınır
  2. Boş MyCollection bir öğe oluşturulur ve önbelleğe alınır
  3. Dizin oluşturucu'nun alıcısı dizinler için önbelleğe alınmış değerlerle çağrılır
  4. GetF1 değerlendirilir ve önceki adımda döndürülen F1'nin C1 alanına atanır.
  5. Dizin oluşturucu'nun alıcısı dizinler için önbelleğe alınmış değerlerle çağrılır
  6. GetF2 değerlendirilir ve önceki adımda döndürülen F2'nin C1 alanına atanır.

Başvuru güvenliği

Koleksiyon ifadeleri başvuru güvenliği bölümü, API'ler genişletilmiş biçimlerinde çağrıldığında parametre koleksiyonlarının oluşturulması için geçerlidir.

Params parametreleri, türleri bir başvuru yapısı olduğunda örtük olarak scoped gösterilir. UnscopedRefAttribute bunu geçersiz kılmak için kullanılabilir.

Meta veriler

Meta verilerde, dizi olmayan params parametrelerini bugün dizilerin System.ParamArrayAttribute ile işaretlendiği gibi params ile işaretleyebiliriz. Ancak, dizi params olmayan parametreler için farklı bir öznitelik kullanmak çok daha güvenli olacaktır. Örneğin, geçerli VB derleyicisi bunları ne normal ne de genişletilmiş biçimde dekore edilmiş ParamArrayAttribute olarak kullanamaz. Bu nedenle, 'params' değiştiricisinin eklenmesi muhtemelen VB tüketicilerini ve yüksek ihtimalle diğer dil veya araçlardaki tüketicileri bozabilir.

Bunu göz önünde bulundurarak, dizi params olmayan parametreler yeni System.Runtime.CompilerServices.ParamCollectionAttributebir ile işaretlenir.

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
    public sealed class ParamCollectionAttribute : Attribute
    {
        public ParamCollectionAttribute() { }
    }
}

Bu bölüm LDM'de gözden geçirildi ve onaylandı.

Açık sorular

Yığın ayırmaları

Şu alıntıdan https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#unresolved-questionsbir alıntı: "Büyük koleksiyonlar için yığın ayırmaları yığını patlatabilir. 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? Belirtimde params Span<T>'yi takip etmeliyiz. Bu teklifi bağlamında soruları yanıtlamamız gerektiği anlaşılıyor.

[Çözüldü] Gizli parametreler scoped

Bir parametreyi params değiştirdiğinde ref struct olarak kabul edilmesi gerektiği şeklinde bir öneri scoped olarak vardı. BCL durumları incelendiğinde, parametrenin kapsamlı olmasını istediğiniz vaka sayısının neredeyse %100 olduğu savunulmaktadır. Gerekli olan birkaç durumda, varsayılan [UnscopedRef] üzerine yazılabilir.

Ancak, yalnızca params değiştiricisinin varlığına bağlı olarak varsayılanı değiştirmek istenmeyebilir. Özellikle, geçersiz kılma/uygulama senaryolarında params değiştiricinin eşleşmesi gerekmez.

Çözüm:

Parametreler örtük olarak kapsama alınmıştır - https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-11-15.md#params-improvements.

[Çözüldü] scoped veya params seçeneklerini tüm geçersiz kılmalar için zorlamayı düşünün

Parametrelerin params varsayılan olarak olması scoped gerektiğini daha önce belirtmiştik. Bununla birlikte, yeniden ifade etme ile ilgili mevcut kurallarımız nedeniyle geçersiz kılmada tuhaf davranışlara yol açar params:

class Base
{
    internal virtual Span<int> M1(scoped Span<int> s1, params Span<int> s2) => throw null!;
}

class Derived : Base
{
    internal override Span<int> M1(Span<int> s1, // Error, missing `scoped` on override
                                   Span<int> s2  // Proposal: Error: parameter must include either `params` or `scoped`
                                  ) => throw null!;
}

taşıma ve geçersiz kılmaları taşıma arasında params davranış farkımız var burada: scoped örtük olarak devralınır ve bununla paramsbirlikte , scoped kendi başına scoped ve her düzeyde tekrarlanmalıdır.

Teklif: Özgün tanım bir params parametresiyse, params parametrelerinin geçersiz kılmalarının scoped veya scoped olarak açıkça belirtilmesini zorunlu kılmalıyız. Başka bir deyişle, s2 içinde Derived , paramsveya her ikisi de scopedolmalıdır.

Çözüm:

scoped veya params uygulanması gerektiğinde, bir params parametresi üzerinde geçersiz kılma durumunda açıkça belirtilmesi gerekecek - params.

[Çözüldü] Gerekli üyelerin varlığı parametre bildirimini params engellemeli mi?

Aşağıdaki örneği göz önünde bulundurun:

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

public class MyCollection1 : IEnumerable<long>
{
    IEnumerator<long> IEnumerable<long>.GetEnumerator() => throw null;
    IEnumerator IEnumerable.GetEnumerator() => throw null;
    public void Add(long l) => throw null;

    public required int F; // Collection has required member and constructor doesn't initialize it explicitly
}

class Program
{
    static void Main()
    {
        Test(2, 3); // error CS9035: Required member 'MyCollection1.F' must be set in the object initializer or attribute constructor.
    }

    // Proposal: An error is reported for the parameter indicating that the constructor that is required
    // to be available doesn't initialize required members. In other words, one is able
    // to declare such a parameter under the specified conditions.
    static void Test(params MyCollection1 a)
    {
    }
}

Çözüm:

Üyeleri bildirim sitesinde parametre required olma uygunluğunu belirlemek için kullanılan oluşturucuya göre doğrulayacağız params - https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-02-21.md#required-members-and-params-parameters.

Alternatifler

Yalnızca için genişleten params alternatif ReadOnlySpan<T> vardır.

Ayrıca, koleksiyon ifadeleri. Herhangi bir koleksiyon türü için. Api'yi koleksiyon türüyle kullanmak için geliştiricinin genişletilmiş [ bağımsız değişken listesinden önce ve ] sonra olmak üzere iki karakter eklemesi yeterlidir. Bunu göz önünde bulundurarak, params ile desteğin genişletilmesi fazlalık olabilir, özellikle diğer dillerin yakında dizi dışı params parametrelerin kullanımını desteklemesi olası olmadığından.