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.
Özelliklerde anahtar kelime
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 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çin
Şampiyonluk sorunu: https://github.com/dotnet/csharplang/issues/8635
Özet
fieldyeni bağlamsal anahtar sözcüğünü kullanarak otomatik olarak oluşturulan bir yedekleme alanına başvurmalarına izin vermek için tüm özellikleri genişletin. Özellikler artık gövdesiz bir tanımlayıcı ile gövdeli bir tanımlayıcı içerebilir.
Motivasyon
Otomatik özellikler, yalnızca destek alanının değerlerini doğrudan ayarlamaya veya elde etmeye izin verir ve bu işleme erişimcilere erişim değiştiricileri yerleştirerek biraz kontrol sağlanabilir. Bazen erişimcilerden birinde veya her ikisinde de gerçekleşenler üzerinde ek denetime sahip olmak gerekir, ancak bu durum kullanıcıları bir destek alanı bildirme yüküyle karşı karşıya getirir. Destekleyici alan adı, ardından, özellik ile uyumlu olmalıdır ve destekleyici alanın kapsamı, sınıfın tamamını kapsar. Bu durum, sınıf içinde erişimcilerin yanlışlıkla atlanmasıyla sonuçlanabilir.
Birkaç yaygın senaryo vardır. Alıcı işlevi içinde, eğer özellik daha önce tanımlanmamışsa gecikmeli başlatma veya varsayılan değerler kullanılmaktadır. Ayarlayıcıda, bir değerin geçerliliğini sağlamak için bir kısıtlama uygulanır veya INotifyPropertyChanged.PropertyChanged olayını tetikleyerek güncellemeleri algılayıp yayabilirsiniz.
Bu gibi durumlarda artık her zaman bir örnek alanı oluşturmanız ve özelliğin tamamını kendiniz yazmanız gerekir. Bu, makul miktarda kod eklemekle kalmaz, aynı zamanda genellikle yalnızca erişimcilerin gövdeleri için kullanılabilir olması istendiğinde, destekleyici alan türün kalan kısmının kapsamına da sızar.
Sözlük
Auto özelliği: "otomatik olarak uygulanan özellik" (§15.7.4) kısaltması. Otomatik özellik üzerindeki erişimcilerin gövdesi yoktur. Uygulama ve yedekleme depolaması derleyici tarafından sağlanır. Otomatik özellikler
{ get; },{ get; set; }veya{ get; init; }'ye sahiptir.Otomatik erişimci: "otomatik uygulanan erişimci" teriminin kısaltması. Bu, gövdesi olmayan bir erişimcidir. Uygulama ve yedekleme depolaması derleyici tarafından sağlanır.
get;,set;veinit;otomatik erişimcilerdir.Tam erişimci: Bu, gövdesi olan bir erişimcidir. Uygulama derleyici tarafından sağlanmaz, ancak yedekleme depolaması hala olabilir (örnekte olduğu gibi
set => field = value;).Alan destekli özellik: Bu özellik, ya erişimci gövdesinde
fieldanahtar sözcüğünü kullanan bir özellik ya da otomatik özelliktir.Yedekleme alanı: Bu, bir özelliğin erişimcilerindeki
fieldanahtar sözcüğü tarafından belirtilen değişkendir ve otomatik olarak uygulanan erişimcilerde de örtük olarak okunur veya yazılır (get;,set;veyainit;).
Ayrıntılı tasarım
init erişimciye sahip özellikler için, set'den ziyade tüm ilgili koşullar init erişimcisine uygulanır.
İki söz dizimi değişikliği vardır:
Özellik bildiriminin destek alanına erişmek için özellik erişimci gövdeleri içinde kullanılabilecek yeni bir bağlam anahtar sözcüğü
fieldvardır (LDM kararı).Özellikler artık otomatik erişimcilerle tam erişimcileri karıştırıp eşleştirebilir (LDM kararı). "Otomatik özellik", erişimcilerinin gövdesi olmayan bir özellik anlamına gelmeye devam edecek. Aşağıdaki örneklerden hiçbiri otomatik özellikler olarak kabul edilmez.
Örnekler:
{ get; set => Set(ref field, value); }
{ get => field ?? parent.AmbientValue; set; }
Her iki erişimci de bütün erişimci olabilir, ve bu durum fieldkullanan bir tanesiyle ya da her ikisiyle gerçekleşebilir.
{ get => field; set => field = value; }
{ get => field; set => throw new InvalidOperationException(); }
{ get => overriddenValue; set => field = value; }
{
get;
set
{
if (field == value) return;
field = value;
OnXyzChanged();
}
}
Sadece get erişimciye sahip olan özellikler ve ifade gövdeli özellikler de fieldkullanabilir.
public string LazilyComputed => field ??= Compute();
public string LazilyComputed { get => field ??= Compute(); }
Yalnızca ayarlanabilir özellikler field'i de kullanabilir.
{
set
{
if (field == value) return;
field = value;
OnXyzChanged(new XyzEventArgs(value));
}
}
Kritik değişiklikler
Özellik erişimci gövdeleri içinde field bağlamsal anahtar sözcüğünün varlığı, potansiyel olarak bozulmaya neden olabilecek bir değişikliktir.
field bir anahtar sözcük olduğundan ve tanımlayıcı olmadığından, yalnızca normal anahtar sözcük kaçış yolu kullanılarak bir tanımlayıcı tarafından "gölgelenebilir": @field. Özellik erişimci gövdeleri içinde bildirilen field adlı tüm tanımlayıcılar, ilk @eklemelerinin ardından, C#'ın 14'ten önceki sürümlerinden güncelleme yaparken oluşabilecek kesintilere karşı korunabilir.
field adlı bir değişken bir özellik erişimcisinde bildirilirse bir hata bildirilir.
14 veya sonraki bir dil sürümünde, birincil ifadefield destek alanına başvuruyorsa, fakat önceki bir dil sürümünde farklı bir simgeye başvuruda bulunacak olsaydı, bir uyarı bildirilir.
Alan hedefli öznitelikler
Otomatik özelliklerde olduğu gibi, erişimcilerinden birinde bir yedekleme alanı kullanan tüm özellikler alan hedefli öznitelikleri kullanabilir:
[field: Xyz]
public string Name => field ??= Compute();
[field: Xyz]
public string Name { get => field; set => field = value; }
Alan hedefli öznitelik, erişimci bir yedekleme alanı kullanmadığı sürece geçersiz kalır:
// ❌ Error, will not compile
[field: Xyz]
public string Name => Compute();
Özellik başlatıcıları
Başlatıcıları olan özellikler field'i kullanabilir. Yedekleme alanı, ayarlayıcı çağrılmak yerine doğrudan başlatılır (LDM kararı).
Başlatıcı için ayarlayıcı çağırmak bir seçenek değildir; başlatıcılar, temel oluşturucular çağrılmadan önce işlenir ve temel oluşturucu çağrılmadan önce herhangi bir örnek yöntemini çağırmak geçersizdir. Bu, yapıların varsayılan başlatma/kesin ataması için de önemlidir.
Bu, başlatma üzerinde esnek denetim sağlar. Ayarlayıcıyı çağırmadan başlatmak istiyorsanız, bir özellik başlatıcısı kullanırsınız. Ayarlayıcıyı çağırarak başlatmak istiyorsanız, özelliğine oluşturucuda bir başlangıç değeri atayın seçeneğini kullanırsınız.
Burada bunun yararlı olduğu bir örnek verilmiştir.
field anahtar sözcüğün, INotifyPropertyChanged deseni için getirdiği zarif çözüm nedeniyle görünüm modelleriyle çok fazla kullanım elde edeceğine inanıyoruz. Görünüm modeli özellik ayarlayıcıları büyük olasılıkla kullanıcı arabirimine veriye bağlı olabilir ve değişiklik izlemeye veya başka davranışları tetikleme olasılığına neden olabilir. Aşağıdaki kodun, IsActive'i HasPendingChangesolarak ayarlamadan true'ın varsayılan değerini başlatması gerekir.
class SomeViewModel
{
public bool HasPendingChanges { get; private set; }
public bool IsActive { get; set => Set(ref field, value); } = true;
private bool Set<T>(ref T location, T value)
{
if (EqualityComparer<T>.Default.Equals(location, value))
return false;
location = value;
HasPendingChanges = true;
return true;
}
}
Bir özellik başlatıcısı ile oluşturucudan atama arasındaki davranış farkı, dilin önceki sürümlerindeki sanal otomatik özelliklerle de görülebilir:
using System;
// Nothing is printed; the property initializer is not
// equivalent to `this.IsActive = true`.
_ = new Derived();
class Base
{
public virtual bool IsActive { get; set; } = true;
}
class Derived : Base
{
public override bool IsActive
{
get => base.IsActive;
set
{
base.IsActive = value;
Console.WriteLine("This will not be reached");
}
}
}
Kurucu fonksiyon ataması
Otomatik özelliklerde olduğu gibi, oluşturucudaki atama varsa (sanal olabilecek) ayarlayıcıyı çağırır ve ayarlayıcı yoksa, doğrudan yedekleme alanına atamaya geri döner.
class C
{
public C()
{
P1 = 1; // Assigns P1's backing field directly
P2 = 2; // Assigns P2's backing field directly
P3 = 3; // Calls P3's setter
P4 = 4; // Calls P4's setter
}
public int P1 => field;
public int P2 { get => field; }
public int P4 { get => field; set => field = value; }
public int P3 { get => field; set; }
}
Yapılarda kesin tanımlama
Oluşturucuda başvurulamasa da, field anahtar sözcüğü tarafından belirtilen yedekleme alanları, diğer yapı alanlarıyla aynı koşullar altında varsayılan başlatmaya ve varsayılan olarak devre dışı bırakılan uyarılara tabidir (LDM karar 1, LDM kararı 2).
Örneğin (bu tanılamalar varsayılan olarak sessizdir):
public struct S
{
public S()
{
// CS9020 The 'this' object is read before all of its fields have been assigned, causing preceding implicit
// assignments of 'default' to non-explicitly assigned fields.
_ = P1;
}
public int P1 { get => field; }
}
public struct S
{
public S()
{
// CS9020 The 'this' object is read before all of its fields have been assigned, causing preceding implicit
// assignments of 'default' to non-explicitly assigned fields.
P2 = 5;
}
public int P2 { get => field; set => field = value; }
}
Referans döndüren özellikler
Otomatik özelliklerde olduğu gibi, field anahtar sözcüğü de başvuru döndüren özelliklerde kullanılamaz. Ref döndüren özellikler ayar erişimcilerine sahip olamazlar, ve ayar erişimcisi olmadan, yalnızca get erişimcisi ve özellik başlatıcısı destek alanına erişebilir. Bu amaç için özel kullanım örnekleri olmadığından, şu an başvuru döndüren özelliklerin otomatik özellikler olarak yazılmaya başlaması için uygun bir zaman değil.
Null Olabilirlik
Null Değer Atanabilir Referans Türleri özelliğinin bir ilkesi, C# dilindeki mevcut geleneksel kodlama desenlerini anlamak ve bu desenlerin etrafında mümkün olduğunca az işlem gerektirecek şekilde hareket etmekti.
field anahtar sözcük teklifi, basit, idiomatik desenlerin, tembel başlatılan özellikler gibi yaygın olarak istenen senaryoları ele almasını sağlar. Null Atanabilir Başvuru Türlerinin bu yeni kodlama desenleriyle iyi bir uyum sağlaması önemlidir.
Hedef:
fieldanahtar sözcük özelliğinin çeşitli kullanım düzenleri için makul bir null güvenlik düzeyi sağlanmalıdır.fieldanahtar sözcüğünü kullanan desenler, sanki dilin daima bir parçasıymış gibi olmalıdır.fieldanahtar sözcük özelliği için mükemmel bir şekilde idiomatik olan kodda Null Atanabilir Başvuru Türlerini etkinleştirmek için kullanıcıya zorluk çıkarmaktan kaçının.
Önemli senaryolardan biri, yavaş başlatılan özelliklerdir:
public class C
{
public C() { } // It would be undesirable to warn about 'Prop' being uninitialized here
string Prop => field ??= GetPropValue();
}
Aşağıdaki nullability kuralları yalnızca field anahtar sözcüğünü kullanan özelliklere değil, aynı zamanda mevcut otomatik özelliklere de uygulanır.
arka plan alanının null olabilme durumu
Yeni terimlerin tanımları için bkz. Sözlük.
yedekleme alanı özelliğiyle aynı türe sahiptir. Ancak, null belirtmesi, özelliğinden farklı olabilir. Bu null atanabilir ek açıklamayı belirlemek için null dayanıklılık kavramını tanıtıyoruz.
Null direnci sezgisel olarak, bir alanın türü için get değerini içeriyor olsa bile, özelliğin default erişim yordamının null güvenliğini koruduğu anlamına gelir.
alan destekli özellik, erişimcisinin özel bir boş değerlendirilebilir çözümlemesi yapılarak get olup olmadığı belirlenmiştir. Bu çözümlemenin yalnızca özellik başvuru türünde olduğunda ve ek açıklama eklenmediği durumlarda gerçekleştirildiğini unutmayın.
- İki ayrı null atanabilir çözümleme geçişi gerçekleştirilir: 'nin null atanabilir ek açıklamasının
fieldaçıklama eklenmediği ve null atanabilir ek açıklamasının ek açıklama eklendiği bir geçiş. Her analizden kaynaklanan null atanabilir tanılamalar kaydedilir. -
Açıklamalı geçişte, açıklama eklenmemiş geçişte bulunmayan null atanabilir bir tanılama varsa, özellik null dayanıklı değildir. Aksi takdirde null dayanıklıdır.
- Uygulama, önce açıklamalı geçişi gerçekleştirerek iyileştirebilir. Bu hiç tanılamayla sonuçlanmazsa, özellik null dayanıklıdır ve açıklamalı olmayan geçiş atlanabilir.
- Özelliğin alma erişimcisi yoksa, (otomatik olarak) null'a dayanıklıdır.
- Get erişimcisi otomatik olarak uygulanırsa, özellik null'a karşı dayanıklı değildir.
Null-forgiving (!) işleçleri, ve #nullable disablegibi #pragma disable warning yönergeler ve gibi <NoWarn>geleneksel proje düzeyi ayarları, null atanabilir bir analizin tanılaması olup olmadığını belirlerken dikkate alınır.
TanılamaSuppressor'lar , null atanabilir bir analizin tanılaması olup olmadığını belirlerken yoksayılır.
Yedekleme alanının null atanabilir ek açıklaması aşağıdaki gibi belirlenir:
- İlişkili özelliğin null atanabilir ek açıklaması açıklamalı veya belirsizse, null atanabilir ek açıklama, ilişkili özelliğin null atanabilir ek açıklamasıyla aynıdır.
- İlişkili özelliğin null atanabilir ek açıklaması ek açıklama eklenmiyorsa:
- Özelliği null-dayanıklı ise, null atanabilir ek açıklama ek açıklama eklenir.
- Özellik null dayanıklı değilse, null atanabilir ek açıklama ek açıklama eklenmez.
Oluşturucu analizi
Şu anda, bir otomatik nitelik, null atanabilir oluşturucu analizisırasında normal bir alana çok benzer şekilde değerlendirilmektedir. Bu işlemi, alan destekli özelliklere, her alan destekli özelliği arka alanı için bir proxy olarak ele alarak genişletiriz.
Bunu gerçekleştirmek için önceki önerilen yaklaşımdan aşağıdaki belirtim dilini güncelleştiriyoruz ( kalın yazıyla yeni dil):
Bir yapıcıdaki her açık veya örtük 'return' ifadesinde, akış durumu ek açıklamaları ve null özellikleriyle uyumsuz olan her üye için bir uyarı veririz. Üye alan tabanlı bir özellikse, bu denetim için destek alanının null atlanabilir ek açıklaması kullanılır. Aksi takdirde, üyenin null atanabilir açıklaması kullanılır. Bu duruma mantıklı bir temsili değer: Eğer dönüş noktasında üyeni kendisine atamak null atanabilirlik uyarısı üretecekse, dönüş noktasında null atanabilirlik uyarısı oluşturulacaktır.
Uyarı
Null dayanıklı özelliğin temel özelliklerinden biri, yedekleme alanının değeri olduğunda bile "doğru" null atanabilirliğe sahip bir değer döndürmesidirdefault. Bunu göz önünde bulundurarak, bu tür özellikler için akış durumunu izlemeyi bile düşünemeyebiliriz. Ancak bu, şu anda değiştirmek için motive edici bir senaryomuz olmayan, özelliklerin null atanabilir analizinde daha büyük bir değişiklik gibi görünüyor.
Bu temelde kısıtlanmış bir yordamlar arası analizdir. Bir oluşturucuyu analiz etmek için bağlama ve "null dayanıklılık" analizinin bağlamsal anahtar sözcüğünü kullanan field ve null atanabilirliğe sahip olmayan aynı türdeki tüm olası get erişimcileri üzerinde yapılması gerektiğini tahmin ediyoruz. Getter gövdeleri genellikle çok karmaşık olmadığından ve "null-dayanıklılık" analizinin türündeki oluşturucu sayısından bağımsız olarak yalnızca bir kez yapılması gerektiğinden, bunun aşırı derecede pahalı olmadığını düşünüyoruz.
Ayarlayıcı analizi
Kolaylık olması için bir set ya da init erişimcisine atıfta bulunmak için "setter" ve "set accessor" terimlerini kullanırız.
Null-dayanıklı olmayan alan destekli özelliklerin ayarlayıcılarının gerçekten yedekleme alanını başlatıp başlatmadığını denetlemeye gerek vardır.
class C
{
string Prop
{
get => field;
// getter is not null-resilient, so `field` is not-annotated.
// We should warn here that `field` may be null when exiting.
set { }
}
public C()
{
Prop = "a"; // ok
}
public static void Main()
{
new C().Prop.ToString(); // no warning, NRE at runtime
}
}
- Özelliğin başlatıcısı varsa, başlatıcıyı ziyaret ettikten sonra ilk akış durumu akış durumuyla aynıdır.
- Aksi takdirde, ilk akış durumu
field = default;tarafından verilen akış durumuyla aynıdır.
Belirleyicideki her açık veya örtük 'return' ifadesinde, yedek alanının akış durumu'in açıklamaları ve boş atanabilirlik öznitelikleriyle uyumsuzsa bir uyarı bildirilir.
Açıklamalar
Bu formülasyon, oluşturuculardaki sıradan alanlara kasıtlı olarak benzerdir. Temel olarak, yalnızca özellik erişimcileri aslında yedekleme alanına başvurabildiğinden, ayarlayıcı, yedekleme alanı için "mini oluşturucu" olarak değerlendirilir.
Normal alanlarda olduğu gibi, genellikle özelliğin oluşturucuda ayarlandığı için başlatıldığını biliyoruz, ancak bu her zaman böyle olmayabilir. yalnızca Prop != null doğru olduğu bir dal içinde geri dönmek de oluşturucu analizimiz için yeterlidir, çünkü özelliği ayarlamak için izlenmeyen mekanizmaların kullanılmış olabileceğini anlıyoruz.
Alternatifler göz önünde bulunduruldu; Nullability alternatifleri bölümüne bakın.
nameof
field anahtar sözcük olduğu yerlerde, nameof(field) derleme başarısız olur (LDM kararı), nameof(nint) gibi. Bu, bazı .NET Core kitaplıklarında özellik ayarlayıcıları ArgumentException oluşturduğunda kullanılacak şey olan nameof(value)gibi değildir. Buna karşılık, nameof(field) beklenen kullanım örnekleri yoktur.
Geçersiz kılmalar
Özellikler geçersiz kılındığında fieldkullanılabilir.
field'un bu tür kullanımları, üstüne yazma özelliğinin destek alanına başvurur ve eğer mevcutsa, temel özelliğin destek alanından ayrıdır. Temel bir özelliğin arka plan alanını devralan sınıflara açan bir ABI yoktur çünkü bu, kapsüllemeyi bozar.
Otomatik özelliklerde olduğu gibi, field anahtar sözcüğünü kullanan ve bir temel özelliği geçersiz kılan özellikler tüm erişimcileri geçersiz kılmalıdır (LDM karar).
Yakalamalar
field yerel işlevlerde ve lambdalarda yakalanabilmelidir ve başka başvurular olmasa bile yerel işlevler ve lambdaların içinden field başvurularına izin verilir (LDM karar 1, LDM kararı 2):
public class C
{
public static int P
{
get
{
Func<int> f = static () => field;
return f();
}
}
}
Alan kullanımı uyarıları
field anahtar sözcüğü bir erişimcide kullanıldığında, derleyicinin atanmamış veya okunmamış alanlar için mevcut çözümlemesi bu alanı içerir.
- CS0414: 'Xyz' özelliği için yedekleme alanı atanır ancak değeri hiçbir zaman kullanılmaz
- CS0649: 'Xyz' özelliği için yedekleme alanı hiçbir zaman atanmamıştır ve her zaman varsayılan değerine sahip olacaktır
Belirtim değişiklikleri
Sözdizimi
Dil sürümü 14 veya daha yüksek ile derlenirken,
-
get,setveiniterişimcilerinin yöntem gövdelerinde, özelliklerinde ama dizin oluşturucularında değil - Bu erişim fonksiyonlarına uygulanan özniteliklerde
- İç içe lambda ifadelerinde, yerel işlevlerde ve bu erişim işlevlerinde LINQ ifadelerinde
Dil sürümü 12 veya daha düşük olan derlemeler de dahil olmak üzere diğer tüm durumlarda field tanımlayıcı olarak kabul edilir.
primary_no_array_creation_expression
: literal
+ | 'field'
| interpolated_string_expression
| ...
;
Özellikler
§15.7.1Özellikleri - Genel
property_initializer yalnızcaotomatik olarak uygulanan bir özellik için ve verilebilir.üretilmiş bir yedekleme alanı olan bir özellik için property_initializer , bu tür özelliklerin temel alanının,ifadesi tarafından verilen değerle başlatılmasına neden olur.
§15.7.4Otomatik olarak uygulanan özellikler
Otomatik olarak uygulanan özellik (veya kısaca otomatik özellik), yalnızca noktalı virgüllü erişimci gövdeleri olan, soyut olmayan, external olmayan, ref değeri olmayan bir özelliktir. Otomatik özellikler bir get erişimciye sahip olmalı ve isteğe bağlı olarak bir set erişimciye sahip olabilir. Bunlardan biri veya her ikisi de:
- Yalnızca noktalı virgülle gövdeye sahip bir aksesuar
özelliğin erişimcilerinde veya ifade gövdesi içinde bağlamsal anahtar sözcüğünün kullanımı Bir özellik otomatik olarak uygulanan bir özellik olarak belirtildiğinde,
özelliği için gizli bir adsız yedekleme alanı otomatik olarak kullanılabilir ve erişimcilerbu yedekleme alanından okumak ve bu alana yazmak için uygulanır. Otomatik özellikler için, yalnızca noktalı virgülle belirtilen bir erişimci, arka alandan okumak için uygulanır ve yalnızca noktalı virgülle belirtilen bir erişimci, arka alana yazmak için uygulanır.
Gizli yedekleme alanına erişilemez, yalnızca otomatik olarak uygulanan özellik erişimcileri aracılığıyla, hatta içeren tür içinde bile okunabilir ve yazılabilir.fieldanahtar sözcüğü kullanılarak doğrudan başvurulabilir. Alan adlandırılmamış olduğundan,nameofifadesinde kullanılamaz.Otomatik özelliğin bir ayarlanmış erişimcisi
yoksa ve yalnızca noktalı virgül içeren bir get erişimcisi varsa, yedekleme alanı ( §15.5.3 ) olarak kabul edilir. Birreadonlyalanı gibi, bir salt okunur otomatik özellik (ayarlanmış erişimci veya init erişimcisi olmadan), kapsayan sınıfın oluşturucusunun gövdesinde de atanabilir. Böyle bir atama, özelliğinsalt okunuryedekleme alanına doğrudan yapılır.Bir otomatik özelliğin,
seterişimcisi olmadan sadece noktalı virgül içerengeterişimcisine sahip olmasına izin verilmez.Bir otomatik özellik, isteğe bağlı olarak, doğrudan arka plan alanına variable_initializer (§17.7) olarak uygulanan bir property_initializer'ye sahip olabilir.
Aşağıdaki örnek:
// No 'field' symbol in scope.
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}
aşağıdaki bildirime eşdeğerdir:
// No 'field' symbol in scope.
public class Point
{
public int X { get { return field; } set { field = value; } }
public int Y { get { return field; } set { field = value; } }
}
şu değere eşdeğerdir:
// No 'field' symbol in scope.
public class Point
{
private int __x;
private int __y;
public int X { get { return __x; } set { __x = value; } }
public int Y { get { return __y; } set { __y = value; } }
}
Aşağıdaki örnek:
// No 'field' symbol in scope.
public class LazyInit
{
public string Value => field ??= ComputeValue();
private static string ComputeValue() { /*...*/ }
}
aşağıdaki bildirime eşdeğerdir:
// No 'field' symbol in scope.
public class Point
{
private string __value;
public string Value { get { return __value ??= ComputeValue(); } }
private static string ComputeValue() { /*...*/ }
}
Alternatifler
Geçersizlik Alternatifleri
Null Atanabilirlik bölümünde özetlenen null dayanıklılığı yaklaşımına ek olarak, çalışma grubu LDM'nin dikkate alması için aşağıdaki alternatifleri önerdi:
Hiçbir şey yapma
Burada hiç özel bir davranış tanıtmayabiliriz. Geçerli olan:
- Alan tarafından desteklenen bir özelliğe, otomatik özelliklere bugün nasıl davranılıyorsa aynı şekilde davranın; gerekli olarak işaretlenmediği sürece oluşturucuda başlatılmalıdır.
- Özellik erişimcileri analiz ederken alan değişkenine özel işlem yapılmaz. Özelliğiyle aynı türde ve null değeri olabilirliğine sahip basit bir değişkendir.
Bu durumun "gecikmeli özellik" senaryoları için rahatsız edici uyarılara neden olacağını unutmayın; bu durumda, kullanıcıların oluşturucu uyarılarını susturmak için büyük olasılıkla null! veya benzerlerini ataması gerekecektir.
Değerlendirebileceğimiz bir "alt alternatif", null atanabilir özellik analizinde field anahtar sözcüğünü kullanarak, özellikleri tamamen yoksaymaktır. Bu durumda, kullanıcının herhangi bir şeyi başlatması gerektiği konusunda hiçbir uyarı olmaz, ancak hangi başlatma desenini kullandığından bağımsız olarak kullanıcı için hiçbir sıkıntı olmaz.
yalnızca .NET 9'da Önizleme LangVersion altında field anahtar sözcük özelliğini göndermeyi planladığımız için, .NET 10'da özellik için null atabilirlik davranışını değiştirme yeteneğine sahip olmayı bekliyoruz. Bu nedenle, kısa vadede bunun gibi "daha düşük maliyetli" bir çözüm benimsemeyi ve uzun vadede daha karmaşık çözümlerden birine kadar büyümeyi düşünebiliriz.
field-hedefli null olabilirlik öznitelikleri
Herhangi bir yordamlar arası analize gerek kalmadan makul düzeyde null güvenlik elde ederek aşağıdaki varsayılanları tanıtabiliriz:
-
fielddeğişkeni, özelliğin null atanabilir ek açıklamasıyla her zaman aynıdır. - Boş değer atanabilirlik öznitelikleri
[field: MaybeNull, AllowNull]vb. arka plan alanının boş değer atanabilirliğini özelleştirmek için kullanılabilir. - alan tabanlı özellikler, alanın null atanabilir açıklamaları ve özniteliklerine göre, oluşturucularda başlatma için denetlenir.
- alan destekli özelliklerdeki setter'lar, constructor'lara benzer şekilde
fieldbaşlatılmasını kontrol eder.
Bu, "little-l tembel senaryosunun" aşağıdaki gibi görüneceği anlamına gelir:
class C
{
public C() { } // no need to warn about initializing C.Prop, as the backing field is marked nullable using attributes.
[field: AllowNull, MaybeNull]
public string Prop => field ??= GetPropValue();
}
Şu nedenle burada null atanabilirlik özelliklerini kullanmaktan uzak durduk: sahip olduğumuz özellikler, gerçekten imza bileşenlerinin giriş ve çıkışlarını tanımlamaya yönelik tasarlanmıştır. Uzun ömürlü değişkenlerin null olabilme durumunu açıklamak için kullanmak hantal görünüyor.
- Pratikte, alanın boş değere atanabilir bir değişken olarak makul şekilde davranmasını sağlamak için
[field: MaybeNull, AllowNull]gereklidir. Bu, olasılıkla boş olan bir başlangıç akış durumunu belirler ve alana olası boş değerlerin yazılmasına izin verir. Kullanıcılardan, nispeten yaygın olan "biraz tembel" senaryolar için bunu yapmalarını istemek zahmetli geliyor. - Bu yaklaşımı izleseydik,
[field: AllowNull]kullanıldığında,MaybeNulleklemeyi öneren bir uyarı eklemeyi düşünebilirdik. Bunun nedeni, AllowNull'in kendi başına null atanabilir bir değişkende kullanıcıların ihtiyaç duyduğu işlevi yerine getirmemesidir: Henüz hiçbir şey yazılmadığında alanın başlangıçta null olmadığını varsayar. - Ayrıca,
[field: MaybeNull]anahtar sözcüğündekifielddavranışını, hatta genel olarak alanlar üzerinde null değerlerin de değişkene yazılması içinAllowNullörtük olarak mevcutmuş gibi ayarlamayı da göz önünde bulundurabiliriz.
öğesinin ilk akış durumunu çıkar field
Davranışı 'nin null atanabilir ek açıklamasıyla fieldtanımlamak yerine 'get' erişimcisinde öğesinin field ilk akış durumunu tanımlayabiliriz. Bu, öğesine olası bir null değer fieldatamanın uygun olduğu gerçeğini yansıtır. Elde ettiğimiz diğer analiz biçimleri, bunun sonucunda ortaya çıkan null güvenlik sorunlarını belirlemede başarılı olacaktır.
Ancak bu, bu değişikliği yapma değişim sıklığının faydalı olmadığı bir uygulama aşamasında ortaya çıktı. Bu, gelecekte çok az kullanıcının herhangi bir molayı algılamasıyla birlikte, akla gelebilecek şekilde ayarlayabileceğiniz bir şeydir; bu alternatif yaklaşım genellikle aslında uyguladığımız yaklaşımdan daha izin vericidir.
Cevaplanmış LDM soruları
Anahtar sözcükler için söz dizimi konumları
field ve value sentezlenmiş bir yedekleme alanına veya örtük bir ayarlayıcı parametresine bağlanabildiği erişimcilerde, hangi söz dizimi konumlarında tanımlayıcılar anahtar sözcükler olarak kabul edilmelidir?
- her zaman
- Yalnızca birincil ifadeler
- hiç
İlk iki durum uyumsuzluk yaratan değişikliklerdir.
Eğer tanımlayıcılar her zaman olarak kabul edilen anahtar sözcüklerse, bu aşağıdaki örnekler için büyük bir değişiklik anlamına gelir:
class MyClass
{
private int field;
public int P => this.field; // error: expected identifier
private int value;
public int Q
{
set { this.value = value; } // error: expected identifier
}
}
Tanımlayıcılar yalnızca birincil ifade olarak kullanıldığında anahtar sözcüklerse, kırılmaya neden olan değişiklik daha küçüktür. En yaygın bozulma, fieldadlı mevcut bir üyenin niteliksiz kullanımı olabilir.
class MyClass
{
private int field;
public int P => field; // binds to synthesized backing field rather than 'this.field'
}
Ayrıca field veya value iç içe yerleştirilmiş bir işleve yeniden ilan edildiğinde de bir kesinti olur. Bu, valuebirincil ifadeler için tek ara olabilir.
class MyClass
{
private IEnumerable<string> _fields;
public bool HasNotNullField
{
get => _fields.Any(field => field is { }); // 'field' binds to synthesized backing field
}
public IEnumerable<string> Fields
{
get { return _fields; }
set { _fields = value.Where(value => Filter(value)); } // 'value' binds to setter parameter
}
}
Tanımlayıcılar asla anahtar sözcük olarak kabul edilmezse, tanımlayıcılar diğer üyelere bağlanmadıkları zaman yalnızca sentez yedekleme alanına veya varsayılan olarak gelen parametreye bağlanır. Bu durum için uyumsuzluk yaratan bir değişiklik yoktur.
Cevap
{ set; } benzer senaryolar
{ set; } şu anda izin verilmiyor ve bu mantıklı: bunun oluşturduğu alan hiçbir zaman okunamıyor. Artık ayarlayıcının hiçbir zaman okunmayan bir yedekleme alanı oluşturmasıyla sonuçlanabilecek yeni yollar bulunmaktadır, örneğin { set; }'ın { set => field = value; }olarak genişletilmesi.
Bu senaryolardan hangisinin derlenmesine izin verilmelidir? "Alan hiçbir zaman okunmaz" uyarısının el ile bildirilen bir alan gibi uygulanacağını varsayalım.
-
{ set; }- Bugün yasak, yasak devam edecek { set => field = value; }{ get => unrelated; set => field = value; }{ get => unrelated; set; }-
{ set { if (field == value) return; field = value; SendEvent(nameof(Prop), value); } } -
{ get => unrelated; set { if (field == value) return; field = value; SendEvent(nameof(Prop), value); } }
Cevap
Bugün zaten otomatik özelliklerde izin verilmeyenlere izin verme, gövdesiz set;.
field olay erişimcisinde
field bir olay erişimcisinde anahtar sözcük olmalı ve derleyici bir yedekleme alanı oluşturmalı mı?
class MyClass
{
public event EventHandler E
{
add { field += value; }
remove { field -= value; }
}
}
Öneri: field bir olay erişimcisinde anahtar sözcük değildir ve hiçbir yedekleme alanı oluşturulmaz.
Cevap
Öneri alındı.
field, olay erişimcisi içinde 'in bir anahtar sözcük olmadığını belirtir ve bu yüzden herhangi bir yedekleme alanı oluşturulmaz.
field geçersiz olabilirliği
Önerilen field null atanabilirliği kabul edilmeli mi?
Nullability bölümüne ve içindeki çözülmemiş soruya bakın.
Cevap
Genel teklif benimsenmiştir. Belirli bir davranışın daha fazla gözden geçirilmesi gerekir.
Özellik başlatıcıda field
field bir özellik başlatıcısında anahtar sözcük olup temel alana bağlanmalıdır?
class A
{
const int field = -1;
object P1 { get; } = field; // bind to const (ok) or backing field (error)?
}
Başlatıcıda yedekleme alanına başvurmak için yararlı senaryolar var mı?
class B
{
object P2 { get; } = (field = 2); // error: initializer cannot reference instance member
static object P3 { get; } = (field = 3); // ok, but useful?
}
Yukarıdaki örnekte, yedekleme alanına bağlama şu hataya neden olmalıdır: "başlatıcı statik olmayan alana başvuramıyor".
Cevap
Başlatıcıyı önceki C# sürümlerinde olduğu gibi bağlayacağız. Destekleme alanını kapsama dahil etmeyeceğiz veya field adlı diğer üyelere başvurmayı engellemeyeceğiz.
Kısmi özelliklerle etkileşim
Başlatıcılar
Kısmi bir özellik fieldkullandığında, hangi bölümlerin başlatıcıya sahip olmasına izin verilmelidir?
partial class C
{
public partial int Prop { get; set; } = 1;
public partial int Prop { get => field; set => field = value; } = 2;
}
- Her iki parçanın da başlatıcısı olduğunda bir hata oluşması gerektiği açıktır.
- Tanım veya uygulama bölümünün
fieldilk değerini ayarlamak isteyebileceği kullanım örnekleri düşünebiliriz. - Tanım bölümünde başlatıcıya izin verirsek, programın geçerli olması için uygulayıcıyı
fieldkullanmaya zorlamış gibi görünüyor. Bu uygun mu? - Uygulamada aynı türde bir yedekleme alanı gerektiğinde oluşturucuların
fieldkullanmasının yaygın olacağını düşünüyoruz. Bunun nedeni oluşturucuların genellikle kullanıcılarının özellik tanımı bölümünde hedeflenen[field: ...]öznitelikleri kullanmasını sağlamak istemeleridir.fieldanahtar sözcüğünü kullanmak, oluşturucu uygulayıcısının bu tür öznitelikleri bazı üretilen alanlara "iletme" ve özellik üzerindeki uyarıları bastırma gereğini ortadan kaldırır. Aynı oluşturucular, kullanıcının alan için bir başlangıç değeri belirtmesine de izin vermek isteyebilir.
Öneri: Uygulama bölümü fieldkullandığında kısmi özelliğin herhangi bir bölümünde başlatıcıya izin verir. Her iki bölümde de başlatıcı varsa, hata bildirin.
Cevap
Öneri kabul edildi. Özellik konumlarını belirlemek veya uygulamak bir başlatıcı kullanabilir, ancak her iki işlem de aynı anda yapılamaz.
Otomatik erişimciler
Başlangıçta tasarlandığı gibi, kısmi özellik uygulaması tüm erişimciler için gövdelere sahip olmalıdır. Ancak, field anahtar sözcük özelliğinin son yinelemeleri "otomatik erişimciler" özelliğini içeriyor. Kısmi özellik uygulamaları bu tür erişimcileri kullanabilmeli mi? Bunlar özel olarak kullanılırsa, tanımlayıcı bir bildirimden ayırt edilemez.
partial class C
{
public partial int Prop0 { get; set; }
public partial int Prop0 { get => field; set => field = value; } // this is equivalent to the two "semi-auto" forms below.
public partial int Prop1 { get; set; }
public partial int Prop1 { get => field; set; } // is this a valid implementation part?
public partial int Prop2 { get; set; }
public partial int Prop2 { get; set => field = value; } // what about this? will there be disagreement about which is the "best" style?
public partial int Prop3 { get; }
public partial int Prop3 { get => field; } // it will only be valid to use at most 1 auto-accessor, when a second accessor is manually implemented.
Öneri: Kısmi özellik uygulamalarında otomatik erişimcilere izin verme, çünkü kullanabilecekleri zamanlardaki sınırlamalar, izin vermenin avantajından daha kafa karıştırıcıdır.
Cevap
En az bir uygulama erişimcisi el ile uygulanmalıdır, ancak diğer erişimci otomatik olarak uygulanabilir.
Yalnızca okunabilir alan
Sentetik yedek alan ne zaman salt okunur olarak kabul edilmelidir?
struct S
{
readonly object P0 { get => field; } = ""; // ok
object P1 { get => field ??= ""; } // ok
readonly object P2 { get => field ??= ""; } // error: 'field' is readonly
readonly object P3 { get; set { _ = field; } } // ok
readonly object P4 { get; set { field = value; } } // error: 'field' is readonly
}
Yedekleme alanı 'salt okunur' kabul edildiğinde, meta veriye yayılan alan olarak işaretlenir ve eğer initonly başlatıcı veya oluşturucu dışında değiştirilirse bir hata bildirilir.
Öneri: İçeren tür bir olduğunda ve özellik veya içeren tür struct olarak bildirildiğinde, birleştirilmiş yedekleme alanı readonly olur.
Cevap
Öneri kabul edilir.
Salt okunabilir bağlam ve set
setkullanan bir özelliğin readonly bağlamında bir field erişimciye izin verilmeli mi?
readonly struct S1
{
readonly object _p1;
object P1 { get => _p1; set { } } // ok
object P2 { get; set; } // error: auto-prop in readonly struct must be readonly
object P3 { get => field; set { } } // ok?
}
struct S2
{
readonly object _p1;
readonly object P1 { get => _p1; set { } } // ok
readonly object P2 { get; set; } // error: auto-prop with set marked readonly
readonly object P3 { get => field; set { } } // ok?
}
Cevap
Bununla ilgili olarak bir set yapısında bir readonly erişimcisi uyguladığınız ve bunu ya geçirebileceğiniz ya da fırlatabileceğiniz senaryolar olabilir. Buna izin vereceğiz.
[Conditional] kodu
field yalnızca koşullu yöntemlere atlandığı çağrılarda kullanıldığında sentezlenmiş alan oluşturulsun mu?
Örneğin, DEBUG olmayan bir derlemede aşağıdakiler için bir yedekleme alanı oluşturulmalı mı?
class C
{
object P
{
get
{
Debug.Assert(field is null);
return null;
}
}
}
Referans olarak, birincil oluşturucu parametrelerinin alanları benzer durumlarda oluşturulur - bkz. sharplab.io.
Öneri: Yedek alan, field yalnızca atlanan koşullu yöntem çağrılarında kullanıldığında otomatik olarak oluşturulur.
Cevap
Conditional kodu, koşullu olmayan kod üzerinde Debug.Assert'in null atanabilirliği değiştirmesi gibi etkiler yaratabilir.
field benzer etkileri olmasaydı garip olurdu. Ayrıca çoğu kodda da ortaya çıkma olasılığı düşüktür, bu nedenle basit bir şey yapacağız ve öneriyi kabul edeceğiz.
Arabirim özellikleri ve otomatik erişim yöntemleri
Manuel ve otomatik olarak uygulanan erişimcilerin kombinasyonu, otomatik uygulanan erişimcinin sentez edilmiş bir yedek alanla ilişkilendirildiği bir interface özelliği için tanınır mı?
Örnek özelliği için örnek alanlarının desteklenmediğini belirten bir hata bildirilir.
interface I
{
object P1 { get; set; } // ok: not an implementation
object P2 { get => field; set { field = value; }} // error: instance field
object P3 { get; set { } } // error: instance field
static object P4 { get; set { } } // ok: equivalent to { get => field; set { } }
}
Öneri: Otomatik erişimciler interface özelliklerde tanınır ve otomatik erişimciler sentezlenmiş bir yedekleme alanına başvurur. Örnek özelliği için örnek alanlarının desteklenmediğini belirten bir hata bildirilir.
Cevap
Hatanın nedeni örnek alanının kendisini standartlaştırmak sınıflardaki kısmi özelliklerle tutarlıdır ve bu sonucu beğeniyoruz. Öneri kabul edilir.
C# feature specifications