Aracılığıyla paylaş


Gerekli Üyeler

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 yakalanır.

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

Şampiyon sayısı: https://github.com/dotnet/csharplang/issues/3630

Özet

Bu teklif, nesne başlatma sırasında bir özelliğin veya alanın ayarlanması gerektiğini belirtmenin bir yolunu ekler ve örnek oluşturucuyu oluşturma sitesindeki nesne başlatıcıdaki üye için bir başlangıç değeri sağlamaya zorlar.

Motivasyon

Günümüzde nesne hiyerarşileri, hiyerarşinin tüm düzeylerinde veri taşımakta çok fazla tekrarlayan kod gerektirir. Şimdi C# 8'de tanımlanabilir bir Person içeren basit bir hiyerarşiye bakalım:

class Person
{
    public string FirstName { get; }
    public string MiddleName { get; }
    public string LastName { get; }

    public Person(string firstName, string lastName, string? middleName = null)
    {
        FirstName = firstName;
        LastName = lastName;
        MiddleName = middleName ?? string.Empty;
    }
}

class Student : Person
{
    public int ID { get; }
    public Student(int id, string firstName, string lastName, string? middleName = null)
        : base(firstName, lastName, middleName)
    {
        ID = id;
    }
}

Burada çok fazla yineleme oluyor:

  1. Hiyerarşinin kökünde, her özelliğin türünün iki kez ve adın dört kez yinelenmeleri gerekiyordu.
  2. Türetilmiş düzeyde, devralınan her özelliğin türü bir kez ve adı iki kez tekrarlanması gerekiyordu.

Bu, 3 özelliğe ve 1 devralma düzeyine sahip basit bir hiyerarşidir, ancak bu hiyerarşi türlerinin birçok gerçek dünya örneği birçok düzey daha derine iner ve bunu yaparken geçirilecek daha büyük ve daha fazla sayıda özelliği bir araya getirir. Roslyn, örneğin CST'lerimizi ve AST'lerimizi oluşturan çeşitli ağaç türlerinde bu tür bir kod tabanıdır. Bu iç içe yerleştirme o kadar yorucudur ki, bu türlerin oluşturucu ve tanımlarını üretmek için kod oluşturucular kullanılır ve birçok müşteri de bu soruna benzer yöntemler uygular. C# 9, bazı senaryolar için bunu daha iyi hale getirebilecek kayıtları tanıtır:

record Person(string FirstName, string LastName, string MiddleName = "");
record Student(int ID, string FirstName, string LastName, string MiddleName = "") : Person(FirstName, LastName, MiddleName);

record, ilk kopyalama kaynağını ortadan kaldırır, ancak ikinci kopyalama kaynağı değişmeden kalır: ne yazık ki, bu, hiyerarşi büyüdükçe artan kopyalama kaynağıdır ve hiyerarşide bir değişiklik yaptıktan sonra bu kopyalamanın en sorunlu kısmıdır çünkü hiyerarşiyi tüm konumlarında takip etmek, hatta büyük olasılıkla projeler arasında ve potansiyel olarak tüketiciler arasında sorun çıkmasına neden olabilir.

Bu tekrarlamayı önlemek amacıyla geçici bir çözüm olarak, tüketicilerin uzun zamandır, oluşturucu yazmaktan kaçınmanın bir yolu olarak nesne başlatıcılarını benimsediklerini gördük. Ancak C# 9'un öncesinde bunun 2 ana dezavantajı vardı:

  1. Nesne hiyerarşisi, her özellik için set erişimcileri ile tamamen modifiye edilebilir olmalıdır.
  2. Grafik üzerinde bir nesnenin her örneğinin her bir üyeyi ayarlamasını sağlamanın hiçbir yolu yoktur.

C# 9, init erişimcisini kullanıma sunarak buradaki ilk sorunu tekrar ele aldı: bununla birlikte, bu özellikler nesne oluşturma/başlatma üzerinde ayarlanabilir, ancak daha sonra ayarlanamaz. Ancak yine ikinci sorun var: C# içindeki özellikler C# 1.0'dan bu yana isteğe bağlı. C# 8.0'da kullanıma sunulan null atanabilir başvuru türleri bu sorunun bir bölümünü ele aldı: Bir oluşturucu null atanamaz bir başvuru türü özelliği başlatmıyorsa, kullanıcı bu konuda uyarılır. Ancak bu, sorunu çözmez: Buradaki kullanıcı, oluşturucuda türünün büyük bölümlerini tekrar etmemek ve özelliklerini tüketicilere ayarlamak amacıyla gereksinimini'e aktarmak istiyor. Ayrıca, IDStudent hakkında herhangi bir uyarı sağlamaz çünkü bu bir değer türüdür. Bu senaryolar, EF Core gibi genel parametresiz bir oluşturucuya sahip olması gereken ve ardından özelliklerin null atanabilirliğine dayalı olarak satırların null atanabilirliğini ayarlayan veritabanı modeli ORM'lerinde oldukça yaygındır.

Bu teklif, C#: gerekli üyelere yeni bir özellik sunarak bu endişeleri gidermeyi ister. Gerekli üyelerin, birden çok oluşturucu ve diğer senaryolar için esneklik sağlamak üzere çeşitli özelleştirmelerle, tür yazarı tarafından değil tüketiciler tarafından başlatılması gerekir.

Ayrıntılı Tasarım

class, structve record türleri required_member_listtanımlama yeteneğine sahiptir. Bu liste,gerekli kabul edilen ve türün bir örneğinin oluşturulması ve başlatılması sırasında başlatılması gereken türdeki tüm özelliklerin ve alanların listesidir. Türler bu listeleri kendi temel türlerinden otomatik olarak devralarak ortak ve yinelenen kodları kaldıran sorunsuz bir deneyim sağlar.

required değiştirici

'required' ve property_modifieriçindeki değiştiriciler listesine ekliyoruz. Bir türün required_member_list, required uygulanmış olan tüm üyelerden oluşur. Bu nedenle, daha önceki Person türü şimdi şöyle görünür:

public class Person
{
    // The default constructor requires that FirstName and LastName be set at construction time
    public required string FirstName { get; init; }
    public string MiddleName { get; init; } = "";
    public required string LastName { get; init; }
}

required_member_list sahip bir türdeki tüm oluşturucular, sözleşmesini otomatik olarak tanıtmalıdır bu türdeki tüketicilerin listedeki tüm özellikleri başlatması gerekir. Oluşturucunun, en az oluşturucunun kendisi kadar erişilebilir olmayan bir üye gerektiren bir sözleşmeyi tanıtması bir hatadır. Örneğin:

public class C
{
    public required int Prop { get; protected init; }

    // Advertises that Prop is required. This is fine, because the constructor is just as accessible as the property initer.
    protected C() {}

    // Error: ctor C(object) is more accessible than required property Prop.init.
    public C(object otherArg) {}
}

required yalnızca class, structve record türlerinde geçerlidir. interface türlerinde geçerli değildir. required aşağıdaki değiştiricilerle birleştirilemez:

  • fixed
  • ref readonly
  • ref
  • const
  • static

required dizin oluşturuculara uygulanmasına izin verilmez.

Derleyici, bir türün zorunlu bir üyesine Obsolete uygulandığında uyarı verecek ve:

  1. Tür Obsoleteişaretlenmemişse veya
  2. SetsRequiredMembersAttribute ile ilişkilendirilmemiş oluşturucular Obsoleteolarak işaretlenmez.

SetsRequiredMembersAttribute

Gerekli üyeleri olan veya temel türü gerekli üyeleri belirleyen bir türdeki tüm oluşturucuların, bu üyelerin oluşturucu çağrıldığında bir tüketici tarafından ayarlanmış olması gerekir. Oluşturucuları bu gereksinimden muaf tutabilmek için, bu gereksinimleri kaldıran SetsRequiredMembersAttributeile bir oluşturucu ilişkilendirilebilir. Oluşturucu gövdesi, türün gerekli üyelerini gerektiği şekilde ayarladığından emin olmak için doğrulanmaz.

SetsRequiredMembersAttribute tüm gereksinimlerini bir oluşturucudan kaldırır ve bu gereksinimler hiçbir şekilde geçerli olup olmadığı denetlenmemektedir. NOT: Geçersiz bir gerekli üye listesi olan bir türden devralma gerekiyorsa çıkış taraması budur: bu türün oluşturucusunu SetsRequiredMembersAttributeile işaretleyin ve hiçbir hata bildirilmeyecektir.

Bir C oluşturucu, baseile atanmış bir this veya SetsRequiredMembersAttribute oluşturucuya zincirleniyorsa, C de SetsRequiredMembersAttributeile atanmış olmalıdır.

Kayıt türü veya herhangi bir temel tür gerekli üyelere sahipse, bir kaydın sentezlenmiş kopya oluşturucusunda SetsRequiredMembersAttribute yayacağız.

NOT: Bu teklifin önceki bir sürümünde başlatmayla ilgili daha büyük bir metalanguage söz konusuydu ve bu sayede bir oluşturucudan tek tek gerekli üyelerin eklenip kaldırılmasına ve oluşturucunun tüm gerekli üyeleri ayarlayıp ayarlamadığını doğrulamaya olanak sağlıyordu. Bu, ilk sürüm için çok karmaşık olarak kabul edildi ve kaldırıldı. Sonraki bir özellik olarak daha karmaşık sözleşmeler ve değişiklikler eklemeye bakabiliriz.

Zorlama

Cigerekli üyelerle T türündeki her oluşturucu R için, Ci çağıran tüketicilerin şunları yapması gerekir:

  • R tüm üyelerini object_creation_expressionüzerinde bir object_initializer içinde ayarlayın.
  • Tüm R üyelerini named_argument_list bölümü aracılığıyla veya attribute_targetayarlayın.

Ci SetsRequiredMembersile ilişkilendirilmediği sürece.

Geçerli bağlam bir object_initializer'e izin vermiyorsa veya attribute_targetdeğilse ve CiSetsRequiredMembersile ilişkilendirilmediyse, Ciçağırmak yanlış olur.

new() kısıtlaması

Parametresiz bir yapıcıya sahip ve sözleşmesini tanıtan bir türün, new()ile kısıtlanmış bir tür parametresi yerine kullanılması, genel örneklemede gereksinimlerin karşılandığından emin olunması için bir yol olmadığından, izin verilmez.

struct defaults

Zorunlu üyeler, struct veya defaultile oluşturulan default(StructType) türlerinin örneklerinde zorunlu kılınmaz. structile oluşturulan new StructType() örnekleri, StructType'nin parametresiz bir oluşturucuya sahip olmadığı ve varsayılan yapı oluşturucusunun kullanıldığı durumlarda bile uygulanmaktadır.

Erişilebilirlik

Üye, içeren türün görünür olduğu herhangi bir bağlamda ayarlanamıyorsa, gerekli üyeyi işaretlemek bir hatadır.

  • Üye bir alansa, readonlyolamaz.
  • Üye bir özellikse, en azından üyenin içeren türü kadar erişilebilir bir ayarlayıcı veya başlatıcıya sahip olmalıdır.

Bu, aşağıdaki durumlara izin verilmediği anlamına gelir:

interface I
{
    int Prop1 { get; }
}
public class Base
{
    public virtual int Prop2 { get; set; }

    protected required int _field; // Error: _field is not at least as visible as Base. Open question below about the protected constructor scenario

    public required readonly int _field2; // Error: required fields cannot be readonly
    protected Base() { }

    protected class Inner
    {
        protected required int PropInner { get; set; } // Error: PropInner cannot be set inside Base or Derived
    }
}
public class Derived : Base, I
{
    required int I.Prop1 { get; } // Error: explicit interface implementions cannot be required as they cannot be set in an object initializer

    public required override int Prop2 { get; set; } // Error: this property is hidden by Derived.Prop2 and cannot be set in an object initializer
    public new int Prop2 { get; }

    public required int Prop3 { get; } // Error: Required member must have a setter or initer

    public required int Prop4 { get; internal set; } // Error: Required member setter must be at least as visible as the constructor of Derived
}

Bir required üyesini gizlemek bir hatadır, bu üye artık bir tüketici tarafından ayarlanamaz.

bir required üyesi geçersiz kılınırken, required anahtar sözcüğü yöntem imzasının üzerine eklenmelidir. Bu, gelecekte bir geçersiz kılma ile bir özelliği gereksiz kılmaya izin vermek istersek, bunu yapabilmek için tasarım alanımızın olmasını sağlamak amacıyla yapılır.

Geçersiz kılmalara, temel türde required olmadığı bir üyenin required olarak işaretlenmesine izin verilir. Gerekli üyeler listesine belirtilmiş bir üye, türetilmiş türe eklenir.

Türlerin gerekli sanal özellikleri geçersiz kılmasına izin verilir. Bu, temel sanal özelliğin depolama alanı varsa ve türetilmiş tür bu özelliğin temel uygulamasına erişmeye çalışırsa başlatılmamış depolamayı gözlemleyebilecekleri anlamına gelir. NOT: Bu genel bir C# anti-pattern örneğidir ve bu teklifin bunu ele alma girişiminde bulunmaması gerektiğini düşünüyoruz.

Boş değer atanabilir analiz üzerindeki etkisi

required olarak işaretlenmiş üyelerin, oluşturucunun sonunda geçerli bir null atanabilir duruma getirilmesi gerekmez. tr-TR: requiredile ilişkilendirilen bir this veya base oluşturucuya zincirleme yapıldığı durumda hariç, bu türdeki ve temel türlerdeki tüm SetsRequiredMembersAttribute üyeleri, null atanabilirlik analizi tarafından bu türdeki herhangi bir oluşturucunun başında varsayılan değer olarak kabul edilir.

Null atanabilirlik analizi, requiredile özniteliklendirilen bir oluşturucu sonunda, geçerli bir null atanabilir duruma sahip olmayan geçerli ve temel türlerdeki tüm SetsRequiredMembersAttribute üyeleri hakkında uyarır.

#nullable enable
public class Base
{
    public required string Prop1 { get; set; }

    public Base() {}

    [SetsRequiredMembers]
    public Base(int unused) { Prop1 = ""; }
}
public class Derived : Base
{
    public required string Prop2 { get; set; }

    [SetsRequiredMembers]
    public Derived() : base()
    {
    } // Warning: Prop1 and Prop2 are possibly null.

    [SetsRequiredMembers]
    public Derived(int unused) : base()
    {
        Prop1.ToString(); // Warning: possibly null dereference
        Prop2.ToString(); // Warning: possibly null dereference
    }

    [SetsRequiredMembers]
    public Derived(int unused, int unused2) : this()
    {
        Prop1.ToString(); // Ok
        Prop2.ToString(); // Ok
    }

    [SetsRequiredMembers]
    public Derived(int unused1, int unused2, int unused3) : base(unused1)
    {
        Prop1.ToString(); // Ok
        Prop2.ToString(); // Warning: possibly null dereference
    }
}

Meta Veri Gösterimi

Aşağıdaki 2 öznitelik C# derleyicisi tarafından bilinir ve bu özelliğin çalışması için gereklidir:

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    public sealed class RequiredMemberAttribute : Attribute
    {
        public RequiredMemberAttribute() {}
    }
}

namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
    public sealed class SetsRequiredMembersAttribute : Attribute
    {
        public SetsRequiredMembersAttribute() {}
    }
}

Bir türe el ile RequiredMemberAttribute uygulamak bir hatadır.

required olarak işaretlenen herhangi bir üyeye bir RequiredMemberAttribute uygulanır. Ayrıca, bu tür üyeleri tanımlayan herhangi bir tür, bu türdeki gerekli üyelerin olduğunu belirtmek için bir işaretçi olarak RequiredMemberAttributeile işaretlenir. Tür BA'den türetilmişse ve A, required üyelerini tanımlar, ancak B herhangi bir yeni üye eklemez veya mevcut required üyelerini geçersiz kılmazsa, BRequiredMemberAttributeile işaretlenmez. Biçinde gerekli üye olup olmadığını tam olarak belirlemek için tam devralma hiyerarşisinin denetlenmesi gerekir.

required üyelerine sahip olan ve ona SetsRequiredMembersAttribute uygulanmamış herhangi bir türdeki oluşturucu, iki öznitelik ile işaretlenmiştir.

  1. System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute, "RequiredMembers"özellik adıyla.
  2. System.ObsoleteAttribute dizesi "Types with required members are not supported in this version of your compiler"ile değiştirildi ve öznitelik hata olarak işaretlendi, böylece eski derleyicilerin bu oluşturucuları kullanması engellenir.

İkili uyumluluğu koruma hedefi olduğundan burada modreq kullanmıyoruz: eğer son required özelliği bir türden kaldırılırsa, derleyici artık bu modreq'yi sentezleyemez, bu da ikili uyumluluğu bozan bir değişiklik olur ve tüm kullanıcıların yeniden derlenmesi gerekir. required üyelerini anlayan bir derleyici bu eski özniteliği yoksayar. Üyelerin de temel türlerden gelebileceğini unutmayın: Geçerli türde yeni required üyeleri olmasa bile, herhangi bir temel türün required üyesi varsa, bu Obsolete özniteliği oluşturulur. Oluşturucunun zaten bir Obsolete özniteliği varsa, ek Obsolete özniteliği oluşturulmaz.

hem ObsoleteAttribute hem de CompilerFeatureRequiredAttribute kullanırız çünkü ikincisi bu sürüm yenidir ve eski derleyiciler bunu anlamaz. Gelecekte, ObsoleteAttribute'ı kaldırabiliriz ve/veya yeni özellikleri korumak için kullanmayı bırakabiliriz, ancak şimdilik tam koruma için her ikisine de ihtiyacımız var.

Tüm temel türler dahil olmak üzere belirli bir requiredtürü için RT üyelerinin tam listesini oluşturmak için aşağıdaki algoritma çalıştırılır:

  1. her Tbiçin, T ile başlayıp object ulaşılana kadar temel tür zincirinde çalışır.
  2. Tb RequiredMemberAttributeile işaretlenmişse, Tb ile işaretlenmiş olan RequiredMemberAttribute'nin tüm üyeleri Rb'e toplanır.
    1. Riiçindeki her Rb için, eğer Ri, R'ün herhangi bir üyesi tarafından geçersiz kılınırsa, atlanır.
    2. Aksi takdirde, herhangi bir RiRüyesi tarafından gizlenirse, gerekli üyelerin aranma işlemi başarısız olur ve başka bir adım atılamaz. T ile ilişkilendirilmemiş SetsRequiredMembers oluşturucusunun çağrılması hataya neden olur.
    3. Aksi takdirde, RiR'e eklenir.

Açık Sorular

İç içe üye başlatıcıları

İç içe üye başlatıcıları için zorlama mekanizmaları ne olacak? Tamamen yasaklanacak mı?

class Range
{
    public required Location Start { get; init; }
    public required Location End { get; init; }
}

class Location
{
    public required int Column { get; init; }
    public required int Line { get; init; }
}

_ = new Range { Start = { Column = 0, Line = 0 }, End = { Column = 1, Line = 0 } } // Would this be allowed if Location is a struct type?
_ = new Range { Start = new Location { Column = 0, Line = 0 }, End = new Location { Column = 1, Line = 0 } } // Or would this form be necessary instead?

Tartışılan Sorular

init yan tümceleri için uygulama düzeyi

init yan tümcesi özelliği C# 11'de uygulanmadı. Bu, etkin bir teklif olmaya devam eder.

Başlangıç değeri olmayan bir init yan tümcesinde belirtilen üyelerin tüm üyeleri başlatmasını kesin bir şekilde zorluyor muyuz? Öyle görünüyor ki bu durumu yapıyoruz, yoksa kolay bir başarısızlık çukuru yaratabiliriz. Bununla birlikte, C# 9'da MemberNotNull ile çözdüğümz sorunların aynısını yeniden ortaya atma riskiyle karşı karşıyayız. Bunu kesin olarak uygulamak istiyorsak, bir yardımcı yöntemin üye ayarlandığını belirtmesi için büyük olasılıkla bir yönteme ihtiyacımız olacaktır. Bunun için ele aldığımız bazı olası söz dizimleri:

  • init yöntemlerine izin verin. Bu yöntemlerin yalnızca bir oluşturucudan veya başka bir init yönteminden çağrılabilmesine izin verilir ve oluşturucuda olduğu gibi this erişebilir (örneğin, readonly ve init alanları/özellikleri ayarlayın). Bu, bu tür yöntemlerdeki init yan tümceleriyle birleştirilebilir. Tümcede belirtilen üye, yöntem/oluşturucu gövdesinde kesin bir şekilde atanmışsa init yan tümcesi karşılanmış sayılır. Bir üyeyi içeren bir init yan tümcesine sahip bir yöntemi çağırmak, bu üyeye atama olarak sayılır. Şayet şimdi veya gelecekte bu yolu izlemenin uygun olduğunu düşünürsek, bir oluşturucuda init yan tümcesi için anahtar sözcük olarak init kullanmamamız gerektiği muhtemeldir çünkü bu kafa karıştırıcı olabilir.
  • ! işlecinin uyarıyı/hatayı açıkça gizlemesine izin verin. Bir üyeyi karmaşık bir şekilde (paylaşılan yöntemde olduğu gibi) başlatırsanız, kullanıcı derleyicinin başlatmayı denetlememesi gerektiğini belirtmek için init yan tümcesine bir ! ekleyebilir.

Sonuç: Tartışmadan sonra ! işleci fikrini beğeniyoruz. Kullanıcının daha karmaşık senaryoları bilinçli bir şekilde ele almasına olanak tanırken, init yöntemleri etrafında büyük bir tasarım problemi yaratmaz ve her yöntemi, üyeleri X veya Y olarak ayarlama amacıyla ek açıklama gerektirmeden kullanılabilir hale getirir. !, zaten null atanabilirlik uyarılarını bastırmak için kullandığımız bir öğedir ve aynı zamanda başka bir bağlamda derleyiciye "Sizden daha akıllıyım" demek için kullanmak, söz diziminin doğal bir uzantısı olduğu için seçilmiştir.

Gerekli arabirim üyeleri

Bu teklif, arabirimlerin üyeleri gerektiği gibi işaretlemesine izin vermez. Bu, şu anda genel türlerdeki new() ve arabirim kısıtlamalarıyla ilgili karmaşık senaryolar bulmak zorunda kalmaktan korur ve hem fabrikalarla hem de genel yapılarla doğrudan ilgilidir. Bu alanda tasarım alanımız olduğundan emin olmak için arabirimlerde required ve required_member_lists olan türlerin new()kısıtlanmış tür parametreleriyle ikame edilmesini yasaklarız. Fabrikalarla genel inşaat senaryolarına daha geniş bir bakış atmak istediğimizde bu sorunu yeniden ele alacağız.

Söz dizimi soruları

init yan tümcesi özelliği C# 11'de uygulanmadı. Bu, etkin bir teklif olmaya devam eder.

  • init doğru kelime mi? init oluşturucuda bir sonek değiştirici olarak kullanmak, fabrikalar için yeniden kullanmak istediğimizde ve ayrıca ön ek değiştiricisi olan init yöntemlerini etkinleştirmek istediğimizde kesintiye uğrayabilir. Diğer olasılıklar:
    • set
  • required, tüm üyelerin başlatıldığını belirtmek için doğru bir değiştirici mi? Diğer önerilenler:
    • default
    • all
    • Bir ! karmaşık mantığı göstermek için
  • base / this ile initarasında bir ayırıcı gerekir mi?
    • : ayırıcı
    • ',' ayırıcısı
  • required doğru değiştirici mi? Önerilen diğer alternatifler:
    • req
    • require
    • mustinit
    • must
    • explicit

Sonuç: şimdilik init yapıcı yan tümcesini kaldırdık ve required'ü özellik değiştirici olarak kullanmaya devam ediyoruz.

Başlatma yan tümcesi kısıtlamaları

init yan tümcesi özelliği C# 11'de uygulanmadı. Bu, etkin bir teklif olmaya devam eder.

Başlatma cümlesindeki this'a erişime izin vermeli miyiz? init'daki atamanın, oluşturucu içinde üyeyi atamanın bir kısaltması olmasını istiyorsak, sanırım öyle yapmalıyız.

Ayrıca, base() gibi yeni bir kapsam mı oluşturuyor yoksa yöntem gövdesiyle aynı kapsamı mı paylaşıyor? Bu özellikle init yan tümcesinin erişmek isteyebileceği yerel işlevler veya bir init ifadesi out parametresi aracılığıyla bir değişken tanıtırsa ad gölgelendirme için önemlidir.

Sonuç: init yan tümcesi kaldırıldı.

Erişilebilirlik gereksinimleri ve init

init yan tümcesi özelliği C# 11'de uygulanmadı. Bu, etkin bir teklif olmaya devam eder.

Bu teklifin init yan tümcesi olan sürümlerinde aşağıdaki senaryoyu elde edebilmek hakkında konuştuk:

public class Base
{
    protected required int _field;

    protected Base() {} // Contract required that _field is set
}
public class Derived : Base
{
    public Derived() : init(_field = 1) // Contract is fulfilled and _field is removed from the required members list
    {
    }
}

Ancak bu noktada tekliften init yan tümcesini kaldırdık, bu nedenle bu senaryoya sınırlı bir şekilde izin verilip verilmeyeceğine karar vermemiz gerekiyor. Sahip olduğumuz seçenekler şunlardır:

  1. Senaryoya izin verme. Bu en muhafazakar yaklaşımdır ve Erişilebilirlik kuralları şu anda bu varsayım düşünülerek yazılmıştır. Kural, gerekli olan herhangi bir üyenin en az içeren türü kadar görünür olması gerektiğidir.
  2. Tüm oluşturucuların şunlardan biri olmasını gerektir:
    1. En az görünen gerekli üyeden daha fazla görünür değil.
    2. SetsRequiredMembersAttribute'ın oluşturucuya uygulanmasını sağlayın. Bunlar, bir oluşturucuyu görebilen herkesin dışarı aktaracağı tüm öğeleri ayarlayabilmesini veya ayarlayabileceğiniz bir şey olmamasını sağlar. Bu, yalnızca statik Create yöntemleri veya benzer oluşturucular aracılığıyla oluşturulan türler için yararlı olabilir, ancak yardımcı program genel olarak sınırlı görünüyor.
  3. Daha önce LDM'de tartışıldığı gibi, sözleşmenin belirli bölümlerini kaldırma yöntemini teklife yeniden ekleyin.

Sonuç: Seçenek 1, gerekli tüm üyelerin en az bulunduğu tür kadar görünürlüğe sahip olması gerekir.

Kuralları geçersiz kıl

Geçerli belirtim, required anahtar sözcüğünün kopyalanması gerektiğini ve geçersiz kılmaların bir üyeyi daha fazla , ancak daha az gerektirebileceğini belirtiyor. Yapmak istediğimiz bu mu? Gereksinimlerin kaldırılmasına izin vermek, şu anda önerdiğimizden daha fazla sözleşme değişikliği yeteneğine ihtiyaç duyar.

Sonuç: Geçersiz kılmaya required'nin eklenmesine izin verilir. Geçersiz kılınan üye requiredise, geçersiz kılan üye de requiredolmalıdır.

Alternatif meta veri gösterimi

Ayrıca, uzantı yöntemlerinden sayfa alarak meta veri gösterimine farklı bir yaklaşım da gösterebiliriz. Türün gerekli üyeler içerdiğini belirtmek için türüne bir RequiredMemberAttribute koyabilir ve ardından gerekli olan her üyeye bir RequiredMemberAttribute koyabiliriz. Bu, arama sırasını basitleştirir (üye araması yapmanız gerekmez, yalnızca özniteliğine sahip üyeleri arayın).

Sonuç: Alternatif onaylı.

Meta Veri Gösterimi

Meta Veri Gösterimi onaylanması gerekir. Ayrıca bu özniteliklerin BCL'ye eklenip eklenmeyeceğine karar vermemiz gerekir.

  1. RequiredMemberAttributeiçin bu öznitelik, null atanabilir/nint/tuple üye adları için kullandığımız genel yerleşik özniteliklere daha çok benzer ve kullanıcı tarafından C#'ta manuel olarak uygulanmaz. Ancak diğer diller bu özniteliği el ile uygulamak isteyebilir.
  2. SetsRequiredMembersAttributeise tüketiciler tarafından doğrudan kullanılır ve bu nedenle büyük olasılıkla BCL'de olmalıdır.

Önceki bölümdeki alternatif temsili kullanırsak, bu durum RequiredMemberAttributeüzerindeki hesaplamayı değiştirebilir: nint/nullable/tuple üye adları için genel gömülü özniteliklere benzemek yerine, uzantı yöntemleri sunulduğundan beri çerçevede yer alan System.Runtime.CompilerServices.ExtensionAttribute'ye daha yakındır.

Sonuç: Her iki özniteliği de BCL'ye ekleyeceğiz.

Uyarı ve Hata Karşılaştırması

Zorunlu bir üyeyi ayarlamamak bir uyarı mı yoksa hata mı olmalı? Sistemi Activator.CreateInstance(typeof(C)) veya benzer şekilde kandırmak kesinlikle mümkündür, bu da tüm özelliklerin her zaman ayarlandığını tam olarak garanti edemeyebiliriz. Ayrıca, genellikle hatalara izin vermediğimiz !kullanarak oluşturucu-sitedeki tanılamaların bastırılmasına da izin veririz. Ancak, özellik salt okunur alanlar veya init özellikleri gibidir, çünkü başlatmadan sonra kullanıcılar böyle bir üyeyi ayarlamaya çalışırsa sistem zorunlu bir hata verir; ama bu yansıma kullanılarak aşılabilir.

Sonuç: Hatalar.