Aracılığıyla paylaş


Kayıtlar

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ına (LDM) aitilgili notlarda kaydedilir.

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

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

Bu teklif, C# dil tasarım ekibi tarafından kabul edilen C# 9 kayıtları özelliğinin belirtimini izler.

Kaydın söz dizimi aşağıdaki gibidir:

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

record_base
    : ':' class_type argument_list?
    | ':' interface_type_list
    | ':' class_type argument_list? ',' interface_type_list
    ;

record_body
    : '{' class_member_declaration* '}' ';'?
    | ';'
    ;

Kayıt türleri, bir sınıf bildirimine benzer başvuru türleridir. record_base bir argument_listiçermiyorsa, kaydın record_declarationparameter_list sağlaması bir hatadır. Kısmi kaydın en fazla bir kısmi tür bildirimi parameter_listsağlayabilir.

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

Miras

Kayıtlar, sınıf objectolmadığı sürece sınıflardan devralamaz ve sınıflar kayıtlardan devralamaz. Kayıtlar diğer kayıtlardan devralabilir.

Kayıt türünün üyeleri

Kayıt gövdesinde bildirilen üyelere ek olarak, bir kayıt türü ek sentezlenmiş üyelere sahiptir. Kayıt 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. Eşleşen üye, derleyicinin başka hiçbir sentezlenmiş üyeyi değil bu üyeyi oluşturmasını engeller. İki üye, aynı imzaya sahipse veya devralma senaryosunda "gizlendiği" kabul edilirse eşleşen olarak kabul edilir. Bir kaydın üyesinin "Clone" olarak adlandırılması hatadır. Bir kaydın örnek alanının en üst düzey işaretçi türüne sahip olması hatadır. bir işaretçi dizisi gibi iç içe işaretçi türüne izin verilir.

Sentezlenen üyeler aşağıdaki gibidir:

Eşitlik üyeleri

Eğer kayıt object'dan türetilmişse, kayıt türü, aşağıdaki gibi özellik olarak bildirilen bir özelliğe eşdeğer sentezlenmiş bir salt okunur özelliği içerir.

Type EqualityContract { get; }

Kayıt türü privateise özelliği sealed. Aksi takdirde özelliği virtual ve protected. özelliği açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa veya açık bildirimin türetilmiş bir türde geçersiz kılınmasına izin vermemesi ve kayıt türünün sealedolmaması bir hatadır.

Kayıt türü Basebir temel kayıt türünden türetilmişse, kayıt türü aşağıdaki gibi bildirilen bir özelliğe eşdeğer bir sentezlenmiş salt okunur özellik içerir:

protected override Type EqualityContract { get; }

özelliği açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa veya açık bildirimin türetilmiş bir türde geçersiz kılınmasına izin vermemesi ve kayıt türünün sealedolmaması bir hatadır. Sentezlenmiş veya açıkça bildirilen herhangi bir özellik, kayıt türü Base'da bu imzaya sahip bir özelliği geçersiz kılmazsa (örneğin, özellik Base'de eksikse, mühürlü veya sanal değilse), bu bir hatadır. Sentezlenen özellik, typeof(R) kayıt türü olduğunda R döndürür.

Kayıt türü System.IEquatable<R>'ı uygular ve Equals(R? other)'nin kayıt türü olduğu durumda sentezlenmiş, güçlü türde bir aşırı yük içeren R'i içerir. Yöntem, public'dir ve kayıt türü virtualolmadığı sürece yöntem sealed'dir. yöntemi açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa veya açık bildirim türetilmiş bir türde geçersiz kılınmasına izin vermiyorsa ve kayıt türü sealeddeğilse hatadır.

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

public virtual bool Equals(R? other);

Sentezlenen Equals(R?), yalnızca aşağıdakilerden her biri trueise true döndürür:

  • other, nulldeğildir ve
  • Devralınmayan kayıt türünde her fieldN örnek alanı için, System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN) alan türü olan TN değeri ve
  • Temel kayıt türü varsa, base.Equals(other) değeri (public virtual bool Equals(Base? other)'e yapılan sanal olmayan bir çağrı); aksi takdirde EqualityContract == other.EqualityContractdeğeri.

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

public static bool operator==(R? left, R? right)
    => (object)left == right || (left?.Equals(right) ?? false);
public static bool operator!=(R? left, R? right)
    => !(left == right);

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 türü Basebir temel kayıt türünden türetilmişse, kayıt türü aşağıdaki gibi bildirilen bir yönteme eşdeğer olan sentetik bir geçersiz kılma içerir:

public sealed override bool Equals(Base? other);

Geçersiz kılma açıkça bildirilirse bu bir hatadır. Yöntem Base kayıt türünde aynı imzaya sahip bir yöntemi geçersiz kılmazsa (örneğin, yöntem Baseeksikse veya korumalıysa veya sanal değilse) bir hatadır. Sentezlenen geçersiz kılma, Equals((object?)other)'ı döndürür.

Kayıt türü, aşağıdaki gibi bildirilen bir yönteme eşdeğer bir sentezlenmiş geçersiz kılma içerir:

public override bool Equals(object? obj);

Geçersiz kılma açıkça bildirilirse bu bir hatadır. Yöntem object.Equals(object? obj) geçersiz kılmazsa (örneğin, ara taban türlerinde gölgeleme nedeniyle vb.) bir hatadır. Sentezlenen geçersiz kılma, Equals(other as R)'in kayıt türü olduğu durumda R döndürür.

Kayıt türü, aşağıdaki gibi bildirilen bir yönteme eşdeğer bir sentezlenmiş geçersiz kılma içerir:

public override int GetHashCode();

yöntemi açıkça bildirilebilir. Açık bildirimin türetilmiş bir türde geçersiz kılınmasına izin vermemesi ve kayıt türünün sealedolmaması bir hatadır. Sentezlenmiş veya açıkça tanımlanmış bir metot, object.GetHashCode()'ı geçersiz kılmazsa (örneğin, ara taban türlerinde gölgeleme nedeniyle) bu bir hatadır.

Equals(R?) ve GetHashCode()'den biri açıkça bildirilirse, ancak diğer yöntem açıkça bildirilmemişse bir uyarı bildirilir.

GetHashCode() sentezlenmiş değiştirme, aşağıdaki değerlerin birleşiminin int sonucunu döndürür:

  • Devralınmayan kayıt türünde her fieldN örnek alanı için, System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) alan türü olan TN değeri ve
  • Bir temel kayıt türü varsa, base.GetHashCode()değeri; aksi takdirde System.Collections.Generic.EqualityComparer<System.Type>.Default.GetHashCode(EqualityContract)değeri.

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

record R1(T1 P1);
record R2(T1 P1, T2 P2) : R1(P1);
record R3(T1 P1, T2 P2, T3 P3) : R2(P1, P2);

Bu kayıt türleri için, sentezlenen eşitlik üyeleri şöyle olacaktır:

class R1 : IEquatable<R1>
{
    public T1 P1 { get; init; }
    protected virtual Type EqualityContract => typeof(R1);
    public override bool Equals(object? obj) => Equals(obj as R1);
    public virtual bool Equals(R1? other)
    {
        return !(other is null) &&
            EqualityContract == other.EqualityContract &&
            EqualityComparer<T1>.Default.Equals(P1, other.P1);
    }
    public static bool operator==(R1? left, R1? right)
        => (object)left == right || (left?.Equals(right) ?? false);
    public static bool operator!=(R1? left, R1? right)
        => !(left == right);
    public override int GetHashCode()
    {
        return HashCode.Combine(EqualityComparer<Type>.Default.GetHashCode(EqualityContract),
            EqualityComparer<T1>.Default.GetHashCode(P1));
    }
}

class R2 : R1, IEquatable<R2>
{
    public T2 P2 { get; init; }
    protected override Type EqualityContract => typeof(R2);
    public override bool Equals(object? obj) => Equals(obj as R2);
    public sealed override bool Equals(R1? other) => Equals((object?)other);
    public virtual bool Equals(R2? other)
    {
        return base.Equals((R1?)other) &&
            EqualityComparer<T2>.Default.Equals(P2, other.P2);
    }
    public static bool operator==(R2? left, R2? right)
        => (object)left == right || (left?.Equals(right) ?? false);
    public static bool operator!=(R2? left, R2? right)
        => !(left == right);
    public override int GetHashCode()
    {
        return HashCode.Combine(base.GetHashCode(),
            EqualityComparer<T2>.Default.GetHashCode(P2));
    }
}

class R3 : R2, IEquatable<R3>
{
    public T3 P3 { get; init; }
    protected override Type EqualityContract => typeof(R3);
    public override bool Equals(object? obj) => Equals(obj as R3);
    public sealed override bool Equals(R2? other) => Equals((object?)other);
    public virtual bool Equals(R3? other)
    {
        return base.Equals((R2?)other) &&
            EqualityComparer<T3>.Default.Equals(P3, other.P3);
    }
    public static bool operator==(R3? left, R3? right)
        => (object)left == right || (left?.Equals(right) ?? false);
    public static bool operator!=(R3? left, R3? right)
        => !(left == right);
    public override int GetHashCode()
    {
        return HashCode.Combine(base.GetHashCode(),
            EqualityComparer<T3>.Default.GetHashCode(P3));
    }
}

Üyeleri kopyala ve çoğalt

Kayıt türü iki kopyalanabilir eleman içerir.

  • Kayıt türünden tek bir parametre alan oluşturucu. "Kopya oluşturucu" olarak adlandırılır.
  • Derleyici tarafından ayrılmış bir ada sahip sentezlenmiş genel parametresiz örnek "clone" yöntemi

Kopyalama oluşturucusunun amacı, durumu parametresinden oluşturulan yeni örneğe kopyalamaktır. Bu oluşturucu, kayıt bildiriminde bulunan örnek alanı/özellik başlatıcılarını çalıştırmaz. Oluşturucu açıkça bildirilmemişse, derleyici tarafından bir oluşturucu sentezlenir. Kayıt mühürlüyse, oluşturucu özel olacak, aksi takdirde korumalı olacaktır. Açıkça bildirilen bir kopya oluşturucu, kayıt mühürlenmediği sürece genel veya korumalı olmalıdır. Oluşturucunun yapması gereken ilk şey, temel kopya oluşturucuyu veya kayıt nesneden devralıyorsa parametresiz nesne oluşturucuyu çağırmaktır. Kullanıcı tanımlı bir kopya oluşturucu bu gereksinimi karşılamayan örtük veya açık bir oluşturucu başlatıcı kullanıyorsa bir hata bildirilir. Bir temel kopya oluşturucu çağrıldıktan sonra, sentezlenmiş kopya oluşturucu kayıt türü içinde örtük veya açıkça bildirilen tüm örnek alanları için değerleri kopyalar. İster açık ister örtük olsun, bir kopya oluşturucunun tek varlığı, varsayılan örnek oluşturucunun otomatik olarak eklenmesini engellemez.

Temel kayıtta sanal bir "clone" yöntemi varsa, sentezlenen "clone" yöntemi bunu geçersiz kılar ve yöntemin dönüş türü şu anda içinde bulunan türdür. Temel kayıt klonlama metodu mühürlüyse bir hata oluşturulur. Temel kayıtta sanal bir "clone" yöntemi yoksa, kayıt korumalı veya soyut olmadığı sürece kopyalama yönteminin dönüş türü içeren türdür ve yöntem sanaldır. İçeren kayıt soyutsa, sentezlenen kopya yöntemi de soyut olur. "clone" yöntemi soyut değilse, bir kopyalama oluşturucusunun çağrısının sonucunu döndürür.

Üyelerin yazdırılması: PrintMembers ve ToString yöntemleri

Kayıt objecttüretilmişse, kayıt aşağıdaki gibi bildirilen bir yönteme eşdeğer bir sentezlenmiş yöntem içerir:

bool PrintMembers(System.Text.StringBuilder builder);

Kayıt türü privateise yöntemi sealed. Aksi takdirde yöntem virtual ve protected'dir.

Yöntemi:

  1. yöntemi varsa ve kayıt yazdırılabilir üyelere sahipse System.Runtime.CompilerServices.RuntimeHelpers.EnsureSufficientExecutionStack() yöntemini çağırır.
  2. kaydın yazdırılabilir üyelerinin her biri için (statik olmayan ortak alan ve okunabilir özellik üyeleri), bu üyenin adını ve ardından " = " ekler ve ardından üyenin değeri ", ", " ile ayrılır
  3. kaydın yazdırılabilir üyeleri varsa true 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.

Kayıt türü Basebir temel kayıttan türetilmişse, kayıt aşağıdaki gibi bildirilen bir yönteme eşdeğer bir sentezlenmiş geçersiz kılma içerir:

protected override bool PrintMembers(StringBuilder builder);

Eğer kaydın yazdırılabilir üyesi yoksa, temel PrintMembers yöntemini builder parametresi ile çağırır ve sonucu döndürür.

Aksi takdirde yöntem:

  1. bir bağımsız değişkenle (PrintMembers parametresi) temel builder yöntemini çağırır.
  2. PrintMembers yöntemi true döndürdüyse, oluşturucuya ", " ekleyin,
  3. kaydın yazdırılabilir üyelerinin her biri için, önce üyenin adını, ardından " = " ve ardından üyenin değerini ekler: this.member (veya değer türleri için this.member.ToString()), ", " ile ayrılarak.
  4. true değerini döndürür.

PrintMembers yöntemi açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa veya açık bildirimin türetilmiş bir türde geçersiz kılınmasına izin vermemesi ve kayıt türünün sealedolmaması bir hatadır.

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

public override string ToString();

yöntemi açıkça bildirilebilir. Açık bildirim beklenen imza veya erişilebilirlikle eşleşmiyorsa veya açık bildirimin türetilmiş bir türde geçersiz kılınmasına izin vermemesi ve kayıt türünün sealedolmaması bir hatadır. Sentezlenmiş veya açıkça tanımlanmış bir metot, object.ToString()'ı geçersiz kılmazsa (örneğin, ara taban türlerinde gölgeleme nedeniyle) bu bir hatadır.

Sentezlenen yöntem:

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

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

record R1(T1 P1);
record R2(T1 P1, T2 P2, T3 P3) : R1(P1);

Bu kayıt türleri için sentezlenmiş yazdırma üyeleri şöyle olabilir:

class R1 : IEquatable<R1>
{
    public T1 P1 { get; init; }
    
    protected virtual bool PrintMembers(StringBuilder builder)
    {
        builder.Append(nameof(P1));
        builder.Append(" = ");
        builder.Append(this.P1); // or builder.Append(this.P1.ToString()); if T1 is 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();
    }
}

class R2 : R1, IEquatable<R2>
{
    public T2 P2 { get; init; }
    public T3 P3 { get; init; }
    
    protected override bool PrintMembers(StringBuilder builder)
    {
        if (base.PrintMembers(builder))
            builder.Append(", ");
            
        builder.Append(nameof(P2));
        builder.Append(" = ");
        builder.Append(this.P2); // or builder.Append(this.P2); if T2 is a value type
        
        builder.Append(", ");
        
        builder.Append(nameof(P3));
        builder.Append(" = ");
        builder.Append(this.P3); // or builder.Append(this.P3); if T3 is a value type
        
        return true;
    }
    
    public override string ToString()
    {
        var builder = new StringBuilder();
        builder.Append(nameof(R2));
        builder.Append(" { ");

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

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

Konumsal kayıt üyeleri

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

Birincil Oluşturucu

Tür bildiriminin değer parametrelerine karşılık gelen imzaya sahip bir genel oluşturucuya sahip bir kayıt türü vardır. Tür için birincil oluşturucu olarak adlandırılır ve varsa örtük olarak bildirilen varsayılan sınıf oluşturucunun bastırılmasına neden olur. Birincil oluşturucunun ve aynı imzaya sahip bir oluşturucunun sınıfta zaten mevcut olması bir hatadır.

Çalışma zamanında birincil oluşturucu

  1. sınıf gövdesinde görünen örnek başlatıcılarını yürütür

  2. varsa, record_base yan tümcesinde sağlanan bağımsız değişkenlerle temel sınıf oluşturucuyu çağırır

Bir kaydın birincil oluşturucusu varsa, "kopya oluşturucu" dışında kullanıcı tanımlı oluşturucuların açık bir this oluşturucu başlatıcısına sahip olması gerekir.

Birincil oluşturucunun ve kaydın üyelerinin parametreleri, argument_list yan tümcesinin record_base kapsamında ve örnek alanlarının veya özelliklerinin başlatıcıları içinde yer alır. Ö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, temel çağrıların ve başlatıcıların günümüzde normal oluşturucularda çalışma şekline benzer şekilde de kullanılabilir.

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

argument_list’da bildirilen ifade değişkenleri, argument_listkapsamı içindedir. Normal bir oluşturucu başlatıcısının bağımsız değişken listesinde olduğu gibi, aynı gölgeleme kuralları uygulanır.

Özellikler

Kayıt türü bildiriminin her bir kayıt parametresi için, adı ve türü değer parametresi bildiriminden alınan karşılık gelen bir genel özellik üyesi vardır.

Kayıt amaçlı:

  • Genel get ve init otomatik özelliği oluşturulur (bkz. ayrı init erişimci belirtimi). Eşleşen türe sahip devralınan abstract özelliği geçersiz kılınır. Devralınan özelliğin geçersiz kılınabilir erişimcileri public, get ve init yoksa bu bir hatadır. Devralınan özellik gizliyse bu bir hatadır.
    Otomatik özellik, karşılık gelen birincil oluşturucu parametresinin değerine ilk değer atanır. Öznitelikler, ilgili kayıt parametresine sözdizimsel olarak uygulanan öznitelikler için property: veya field: hedefleri kullanılarak, oluşturulmuş otomatik özelliğe ve onun destekleyici alanına uygulanabilir.

Parçalarına ayır

En az bir parametreye sahip konumsal kayıt, birincil oluşturucu bildiriminin her parametresi için out parametre bildirimi ile 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önteminin gövdesi, Deconstruct yönteminin her parametresine, aynı ada sahip olan örnek özelliğinin değerini atar. yöntemi açıkça bildirilebilir. Açık bildirimin beklenen imza veya erişilebilirlikle eşleşmemesi veya statik olması bir hatadır.

Aşağıdaki örnek, derleyici tarafından sentezlenmiş R yöntemiyle ve kullanımıyla birlikte Deconstruct konumsal kaydı göstermektedir:

public record R(int P1, string P2 = "xyz")
{
    public void Deconstruct(out int P1, out string P2)
    {
        P1 = this.P1;
        P2 = this.P2;
    }
}

class Program
{
    static void Main()
    {
        R r = new R(12);
        (int p1, string p2) = r;
        Console.WriteLine($"p1: {p1}, p2: {p2}");
    }
}

with ifadesi

with ifadesi, aşağıdaki söz dizimini kullanan yeni bir ifadedir.

with_expression
    : switch_expression
    | switch_expression 'with' '{' member_initializer_list? '}'
    ;

member_initializer_list
    : member_initializer (',' member_initializer)*
    ;

member_initializer
    : identifier '=' expression
    ;

with ifadesinin bir deyim olarak kullanılmasına izin verilmez.

with ifadesi, member_initializer_listatamalarında yapılan değişikliklerle alıcı ifadesinin bir kopyasını üretmek için tasarlanan "yıkıcı olmayan mutasyona" olanak tanır.

with ifadesinin geçerli sayılması için alıcısının void olmayan bir türe sahip olması gerekir. Alıcı türü bir kayıt olmalıdır.

ifadesinin sağ tarafında,tanımlayıcısına bir atama dizisi içeren bir bulunur ve bu tanımlayıcı, alıcı türünün erişilebilir bir örnek alanı veya özelliği olmalıdır.

İlk olarak, alıcının "clone" yöntemi (yukarıda belirtilen) çağrılır ve sonucu alıcının türüne dönüştürülür. Ardından her member_initializer, dönüştürme sonucunun bir alana veya özellik erişimine yapılan atamayla aynı şekilde işlenir. Atamalar alfabetik sırayla işlenir.