Aracılığıyla paylaş


Kayıt yapıları

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, ilgili dil tasarım toplantısı (LDM) notlarında belirtilmiştir.

Ö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/4334

Kayıt yapısının söz dizimi aşağıdaki gibidir:

record_struct_declaration
    : attributes? struct_modifier* 'partial'? 'record' 'struct' identifier type_parameter_list?
      parameter_list? struct_interfaces? type_parameter_constraints_clause* record_struct_body
    ;

record_struct_body
    : struct_body
    | ';'
    ;

Kayıt yapısı türleri, diğer yapı türleri gibi değer türleridir. Onlar örtülü olarak System.ValueTypesınıfından devralır. Kayıt yapısının değiştiricileri ve üyeleri, yapılarla aynı kısıtlamalara tabidir (türdeki erişilebilirlik, üyelerde değiştiriciler, base(...) örnek oluşturucu başlatıcıları, oluşturucudaki this için kesin atama, yıkıcılar, ...). Kayıt yapıları, parametresiz örnek oluşturucuları ve alan başlatıcıları için yapılarla aynı kuralları da izler, ancak bu belgede yapılar için bu kısıtlamaları genel olarak kaldıracağımız varsayılır.

Bkz. §16.4.9parametresiz yapı oluşturucuları belirtimi.

Kayıt yapıları ref değiştiriciyi kullanamaz.

Bir kısmi kayıt yapısının kısmi tür bildirimlerinden en fazla biri parameter_listsağlayabilir. parameter_list boş olabilir.

Kayıt yapısı parametreleri ref, out veya this değiştiricileri kullanamaz (ancak in ve params izin verilir).

Kayıt yapısının üyeleri

Kayıt yapısı gövdesinde bildirilen üyelere ek olarak, bir kayıt yapısı türü ek sentezlenmiş üyelere sahiptir. Kayıt yapısı gövdesinde "eşleşen" imzaya sahip bir üye bildirilmediği veya "eşleşen" imzaya sahip erişilebilir somut sanal olmayan bir üye devralınmadığı sürece üyeler sentezlenir. İki üye, aynı imzaya sahipse veya devralma senaryosunda "gizlendiği" kabul edilirse eşleşen olarak kabul edilir. Bkz. İmzalar ve aşırı yüklenme §7.6. Bir kayıt yapısının üyesinin "Clone" olarak adlandırılması hatadır.

Bir kayıt yapısının örnek alanının güvenli olmayan bir türe sahip olması hatadır.

Kayıt yapısının bir sonlandırıcı tanımlamasına izin verilmez.

Sentezlenen üyeler aşağıdaki gibidir:

Eşitlik üyeleri

Sentezlenen eşitlik üyeleri bir kayıt sınıfındakine benzer (bu tür içinEquals, Equals türü için object, bu tür için == ve != işleçleri),
EqualityContracteksikliği dışında null denetimleri veya kalıtım.

Kayıt yapısı, System.IEquatable<R>'ı uygular ve kayıt yapısı olan Equals(R other)'nin, R için sentezlenmiş güçlü türde bir aşırı yüklemesini içerir. yöntemi public. yöntemi açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa bu bir hatadır.

Equals(R other) kullanıcı tanımlıysa (sentezlenmediyse) ancak GetHashCode değilse, bir uyarı oluşturulur.

public readonly bool Equals(R other);

Sentezlenmiş Equals(R), ancak ve ancak, kayıt yapısındaki her bir örnek alan true'nin fieldN alan türündeki System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN) değerinin TNolması durumunda true döndürür.

Kayıt yapısı, aşağıdaki gibi bildirilen işleçlere eşdeğer sentezlenmiş == ve != işleçleri içerir:

public static bool operator==(R r1, R r2)
    => r1.Equals(r2);
public static bool operator!=(R r1, R r2)
    => !(r1 == r2);

Equals işleci tarafından çağrılan == yöntemi, yukarıda belirtilen Equals(R other) yöntemidir. != işleci == işlemcisine yetki verir. İşleçler açıkça bildirilirse bu bir hatadır.

Kayıt yapısı, aşağıdaki gibi bildirilen bir yönteme eşdeğer olan otomatik oluşturulmuş bir geçersiz kılma yöntemi içerir.

public override readonly bool Equals(object? obj);

Geçersiz kılma açıkça bildirildiğinde bu bir hatadır. Sentezlenen geçersiz kılma, other is R temp && Equals(temp) bir kayıt yapısı olduğunda R döndürür.

Kayıt yapısı, aşağıdaki gibi bildirilen bir yönteme eşdeğer olan otomatik oluşturulmuş bir geçersiz kılma yöntemi içerir.

public override readonly int GetHashCode();

yöntemi açıkça bildirilebilir.

Equals(R) ve GetHashCode()'den biri açıkça deklar edilir ancak diğeri açıkça deklar edilmemişse bir uyarı bildirilir.

GetHashCode() sentezlenmiş geçersiz kılma, int her örnek alanı için System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) değerlerini fieldN'ın TNtürü olduğu durumda birleştirerek fieldN sonucunu verir.

Örneğin, aşağıdaki kayıt yapısını göz önünde bulundurun:

record struct R1(T1 P1, T2 P2);

Bu kayıt yapısı için, otomatik olarak oluşturulan eşitlik üyeleri şöyle olacaktır:

struct R1 : IEquatable<R1>
{
    public T1 P1 { get; set; }
    public T2 P2 { get; set; }
    public override bool Equals(object? obj) => obj is R1 temp && Equals(temp);
    public bool Equals(R1 other)
    {
        return
            EqualityComparer<T1>.Default.Equals(P1, other.P1) &&
            EqualityComparer<T2>.Default.Equals(P2, other.P2);
    }
    public static bool operator==(R1 r1, R1 r2)
        => r1.Equals(r2);
    public static bool operator!=(R1 r1, R1 r2)
        => !(r1 == r2);    
    public override int GetHashCode()
    {
        return Combine(
            EqualityComparer<T1>.Default.GetHashCode(P1),
            EqualityComparer<T2>.Default.GetHashCode(P2));
    }
}

Yazdırma üyeleri: PrintMembers ve ToString yöntemleri

Kayıt yapısı, aşağıdaki gibi bildirilen bir yönteme eşdeğer bir sentezlenmiş yöntem içerir:

private bool PrintMembers(System.Text.StringBuilder builder);

yöntemi aşağıdakileri yapar:

  1. kayıt yapısının yazdırılabilir her bir üyesi için (statik olmayan ortak alanlar ve okunabilir özellik üyeleri), o üyenin adını, ardından " = ", ve üyenin değerini ekler ve bunları ", " ile ayırır.
  2. kayıt yapısı yazdırılabilir üyelere sahipse 'doğru' değerini döndürür.

Değer türüne sahip bir üye için, hedef platformda kullanılabilen en verimli yöntemi kullanarak değerini dize gösterimine dönüştüreceğiz. Şu anda bu, ToString'e geçmeden önce StringBuilder.Append'ı çağırmak anlamına gelir.

Kaydın yazdırılabilir üyeleri,readonlyget olmayan bir erişimciye sahip okunabilir bir özellik içermiyorsa, sentezlenen PrintMembersreadonlyolur. Kaydın alanlarının readonly olması, PrintMembers yönteminin readonlyolması için gerekli değildir.

PrintMembers yöntemi açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa bu bir hatadır.

Kayıt yapısı, aşağıdaki gibi bildirilen bir yönteme eşdeğer bir sentezlenmiş yöntem içerir:

public override string ToString();

Kayıt yapısının PrintMembers yöntemi readonlyise, sentezlenen ToString() yöntemi readonlyolur.

yöntemi açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa bu bir hatadır.

Sentezlenen yöntem:

  1. bir StringBuilder örneği oluşturur,
  2. kayıt yapısı adını oluşturucuya ekler ve ardından " { ",
  3. kayıt yapısının PrintMembers yöntemini çağırarak oluşturucuyu verir ve ardından " " " true döndürdüyse,
  4. ekler "}",
  5. builder.ToString()ile yapıcının içeriğini döndürür.

Örneğin, aşağıdaki kayıt yapısını göz önünde bulundurun:

record struct R1(T1 P1, T2 P2);

Bu kayıt yapısı için, sentezlenen yazdırma üyeleri şöyle olabilir:

struct R1 : IEquatable<R1>
{
    public T1 P1 { get; set; }
    public T2 P2 { get; set; }

    private bool PrintMembers(StringBuilder builder)
    {
        builder.Append(nameof(P1));
        builder.Append(" = ");
        builder.Append(this.P1); // or builder.Append(this.P1.ToString()); if P1 has a value type
        builder.Append(", ");

        builder.Append(nameof(P2));
        builder.Append(" = ");
        builder.Append(this.P2); // or builder.Append(this.P2.ToString()); if P2 has a value type

        return true;
    }

    public override string ToString()
    {
        var builder = new StringBuilder();
        builder.Append(nameof(R1));
        builder.Append(" { ");

        if (PrintMembers(builder))
            builder.Append(" ");

        builder.Append("}");
        return builder.ToString();
    }
}

Konum kayıt yapısı üyeleri

Yukarıdaki üyelere ek olarak, parametre listesi ("konumsal kayıtlar") içeren kayıt yapıları, yukarıdaki üyelerle aynı koşullara sahip ek üyeleri sentezler.

Temel Yapıcı

Kayıt yapısı, imzası tür bildiriminin değer parametrelerine karşılık gelen bir ortak oluşturucuya sahiptir. Bu, tür için birincil oluşturucu olarak adlandırılır. Birincil oluşturucunun ve yapıda aynı imzaya sahip bir oluşturucunun zaten mevcut olması bir hatadır. Tür bildirimi bir parametre listesi içermiyorsa, birincil oluşturucu oluşturulmaz.

record struct R1
{
    public R1() { } // ok
}

record struct R2()
{
    public R2() { } // error: 'R2' already defines constructor with same parameter types
}

Bir kayıt yapısı için örnek alanı bildirimlerinin değişken başlatıcıları içermesine izin verilir. Birincil oluşturucu yoksa, örnek başlatıcıları parametresiz oluşturucunun bir parçası olarak yürütülür. Aksi takdirde, çalışma zamanında birincil oluşturucu kayıt yapısı gövdesinde görünen örnek başlatıcılarını yürütür.

Bir kayıt yapısı birincil oluşturucuya sahipse, kullanıcı tanımlı oluşturucuların birincil oluşturucuyu veya açıkça bildirilen bir oluşturucuyu çağıran açık bir this oluşturucu başlatıcısı olmalıdır.

Birincil oluşturucunun ve kayıt yapısının üyelerinin parametreleri, örnek alanlarının veya özelliklerinin başlatıcıları içinde kapsam içindedir. Örnek üyeleri bu konumlarda bir hata olabilir (örnek üyelerinin bugün normal oluşturucu başlatıcılarında kapsamda yer almalarına benzer, ancak kullanılması gereken bir hatadır), ancak birincil oluşturucunun parametreleri kapsam içinde ve kullanılabilir olur ve üyeleri gölgeler. Statik üyeler de kullanılabilir.

Birincil oluşturucunun parametresi okunmazsa bir uyarı oluşturulur.

Yapı örneği oluşturucuları için kesin atama kuralları, kayıt yapılarının birincil oluşturucusna uygulanır. Örneğin, aşağıdaki bir hatadır:

record struct Pos(int X) // definite assignment error in primary constructor
{
    private int x;
    public int X { get { return x; } set { x = value; } } = X;
}

Özellikler

Kayıt yapısı bildiriminin her kayıt yapısı parametresi için, adı ve türü değer parametresi bildiriminden alınan ilgili ortak özellik üyesi vardır.

Kayıt yapısı için:

  • Kayıt yapısında get değiştirici varsa, genel init ve readonly otomatik özelliği oluşturulur, aksi takdirde get ve set. Her iki tür küme erişimcisi (set ve init) "eşleştirme" olarak kabul edilir. Bu nedenle kullanıcı, sentezlenmiş değiştirilebilir bir özellik yerine yalnızca init özelliğini bildirebilir. Eşleşen türe sahip devralınan abstract özelliği geçersiz kılınır. Kayıt yapısının, beklenen ada ve türe sahip bir örnek alanı varsa otomatik özellik oluşturulmaz. Devralınan özelliğin publicget ve set/init erişimcileri yoksa bu bir hatadır. Devralınan özellik veya alan gizliyse bu bir hatadır.
    Otomatik özellik, eşdeğer birincil oluşturucu parametresinin başlangıç değerine ayarlanır. Öznitelikler, sırasal olarak ilgili kayıt yapısı parametresine uygulanmış öznitelikler için property: veya field: hedefleri kullanılarak, sentezlenen otomatik özelliğe ve onun destek alanına uygulanabilir.

Parçalarına ayır

En az bir parametreye sahip konumsal kayıt yapısı, birincil oluşturucu bildiriminin her parametresi için out parametre bildirimiyle Deconstruct adlı genel bir void-dönüş örneği yöntemini sentezler. Deconstruct yönteminin her parametresi, birincil oluşturucu bildiriminin karşılık gelen parametresiyle aynı türe sahiptir. Yöntem gövdesi, Deconstruct yönteminin her bir parametresini, aynı ada sahip bir üyeye, bir örnek üyesi erişiminden elde edilen değere atar. Gövdede erişilen örnek üyeler arasındareadonlyget olmayan bir erişimciye sahip bir özellik yoksa, sentezlenen Deconstruct yöntemi readonlyolur. yöntemi açıkça bildirilebilir. Açık bildirimin beklenen imza veya erişilebilirlikle eşleşmemesi veya statik olması bir hatadır.

Yapılarda with ifadesine izin ver

Artık with ifadesindeki alıcının yapı türüne sahip olması geçerlidir.

with ifadesinin sağ tarafında, alıcı türüne ait erişilebilir bir örnek alanı veya özellik olması gereken member_initializer_list'e bir atama dizisi içeren bir bulunmaktadır.

Yapı türünden bir alıcı için, alıcı önce kopyalanır, ardından her member_initializer, dönüştürme sonucunun bir alan veya özellik erişimi ataması gibi aynı şekilde işlenir. Atamalar alfabetik sırayla işlenir.

Kayıtlarda iyileştirmeler

record class izin ver

Kayıt türleri için mevcut söz dizimi, record classile aynı anlama sahip olan record'yi sağlar.

record_declaration
    : attributes? class_modifier* 'partial'? 'record' 'class'? identifier type_parameter_list?
      parameter_list? record_base? type_parameter_constraints_clause* record_body
    ;

Kullanıcı tanımlı pozisyon tabanlı üyelerin alan olarak kullanılmasını sağlama

Bkz. https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-05.md#changing-the-member-type-of-a-primary-constructor-parameter

Beklenen ad ve türe sahip bir örnek alan kayıtta varsa veya devralınmışsa, otomatik özellik oluşturulmaz.

Yapılarda parametresiz oluşturuculara ve üye başlatıcılara izin ver

Bkz. parametresiz yapı oluşturucuları özelliği.

Açık sorular

  • meta verilerdeki kayıt yapılarını tanıma (Yararlanabileceğimiz gizli bir klon yöntemi yok...)

Yanıtladı

  • PrintMembers tasarımını korumak istediğimizi onaylayın (booldöndüren ayrı yöntem ) (yanıt: evet)
  • record ref struct izin vermeyacağımızı onaylayın (IEquatable<RefStruct> ve başvuru alanlarıyla ilgili sorun) (yanıt: evet)
  • eşitlik üyelerinin uygulanmasını onaylayın. Alternatif olarak, bool Equals(R other)sentezlenmiş bool Equals(object? other) ve işleçlerin tümü yalnızca ValueType.Equalsiçin temsilcidir. (yanıt: evet)
  • birincil oluşturucu olduğunda alan başlatıcılarına izin vermek istediğimizi onaylayın. Biz bu aşamadayken parametresiz yapı oluşturucularına da izin vermek istiyor muz (Activator sorunu görünüşe göre düzeltildi)? (yanıt: evet, güncellenmiş spesifikasyon LDM'de gözden geçirilmelidir)
  • Combine yöntemi hakkında ne kadar söylemek istiyoruz? (yanıt: mümkün olduğunca az)
  • Kopya oluşturucu imzası olan kullanıcı tanımlı bir oluşturucuya izin vermemeli miyiz? (yanıt: hayır, kayıt yapılarında kopya oluşturucu belirtimi yoktur)
  • "Clone" adlı üyeleri reddetmek istediğimizi onaylayın. (yanıt: doğru)
  • sentezlenmiş Equals mantığının, çalışma zamanı uygulamasıyla (örn. float.NaN) işlevsel olarak eşdeğer olup olmadığını bir kez daha denetleyin. (yanıt: LDM'de onaylandı)
  • alan veya özellik hedefleme öznitelikleri konumsal parametre listesine yerleştirilebilir mi? (yanıt: evet, kayıt sınıfı için olduğu gibi)
  • Generiklerde with? (yanıt: C# 10 için kapsam dışında)
  • GetHashCode, record struct S1; ile record struct S2;arasında farklı değerler elde etmek için kendi türünün bir karmasını içermeli mi? (yanıt: hayır)