C 'de null yapılabilir başvuru türleri #Nullable reference types in C#

Bu özelliğin amacı:The goal of this feature is to:

  • Geliştiricilerin bir değişkenin, parametrenin veya başvuru türü sonucunun null olması amaçlanıp sunmadığını ifade etmelerine izin verin.Allow developers to express whether a variable, parameter or result of a reference type is intended to be null or not.
  • Bu amaca göre bu tür değişkenler, parametreler ve sonuçlar kullanılmazsa Uyarılar sağlayın.Provide warnings when such variables, parameters and results are not used according to that intent.

Amaç ifadesiExpression of intent

Dil, T? değer türleri için söz dizimini zaten içeriyor.The language already contains the T? syntax for value types. Bu söz dizimini başvuru türlerine genişletmek basittir.It is straightforward to extend this syntax to reference types.

Bu, donatılamayan bir başvuru türü hedefinin null olmadığı varsayılır T .It is assumed that the intent of an unadorned reference type T is for it to be non-null.

Null yapılabilir başvuruların denetlenmesiChecking of nullable references

Akış Analizi, null yapılabilir başvuru değişkenlerini izler.A flow analysis tracks nullable reference variables. Analiz, bu kişilerin null olmaması (örneğin, bir denetim veya atamadan sonra), bu değerin null olmayan bir başvuru olarak kabul edilir.Where the analysis deems that they would not be null (e.g. after a check or an assignment), their value will be considered a non-null reference.

x!Akış Analizi, geliştiricinin orada olduğunu belirten null olmayan bir durum kullanamayabileceği için, null olmayan bir başvuru, sonek işleci ("damnit" işleci) ile açık olmayan olarak kabul edilebilir.A nullable reference can also explicitly be treated as non-null with the postfix x! operator (the "damnit" operator), for when flow analysis cannot establish a non-null situation that the developer knows is there.

Aksi takdirde, bir null yapılabilir başvuruya başvuruluyorsa veya null olmayan bir türe dönüştürülürse bir uyarı verilir.Otherwise, a warning is given if a nullable reference is dereferenced, or is converted to a non-null type.

S[] T?[] ' Dan ' A ve ' A dönüştürme sırasında bir uyarı verilir S?[] T[] .A warning is given when converting from S[] to T?[] and from S?[] to T[].

C<S> C<T?> Tür parametresi birlikte değişken ( out ) olduğunda ve C<S?> C<T> tür parametresi değişken karşıtı () olduğunda ' dan ' A dönüştürülürken in bir uyarı verilir.A warning is given when converting from C<S> to C<T?> except when the type parameter is covariant (out), and when converting from C<S?> to C<T> except when the type parameter is contravariant (in).

C<T?>Tür parametresinde null olmayan kısıtlamalar varsa, üzerinde bir uyarı verilir.A warning is given on C<T?> if the type parameter has non-null constraints.

Null olmayan başvuruların denetlenmesiChecking of non-null references

Null olmayan bir değişkene atandığında veya null olmayan bir parametre olarak geçirilmemişse bir uyarı verilir.A warning is given if a null literal is assigned to a non-null variable or passed as a non-null parameter.

Oluşturucu null olmayan başvuru alanlarını açıkça başlatmadıysanız bir uyarı da verilir.A warning is also given if a constructor does not explicitly initialize non-null reference fields.

Null olmayan başvuruların dizisinin tüm öğelerinin başlatıldığını yeterince izleyemedik.We cannot adequately track that all elements of an array of non-null references are initialized. Ancak, yeni oluşturulan bir dizinin bir öğesi, dizi okunmadan veya geçirilmeden önce öğesine atanmamışsa bir uyarı verebilir.However, we could issue a warning if no element of a newly created array is assigned to before the array is read from or passed on. Bu, çok gürültülü olmayan ortak durumu işleyebilir.That might handle the common case without being too noisy.

Uyarı oluşturulup oluşturulmayacağını default(T) veya yalnızca tür olarak değerlendirilip değerlendirilmeyeceğine karar vermemiz gerekiyor T? .We need to decide whether default(T) generates a warning, or is simply treated as being of the type T?.

Meta veri gösterimiMetadata representation

Null olabilme manları öznitelik olarak meta verilerde temsil edilmelidir.Nullability adornments should be represented in metadata as attributes. Bu, alt düzey derleyicilerin bunları yoksayması anlamına gelir.This means that downlevel compilers will ignore them.

Yalnızca null yapılabilir ek açıklamaların dahil edilip edilmeyeceğini veya derlemede null olmayan "açık" olup olmadığını belirlemek istiyoruz.We need to decide if only nullable annotations are included, or there's also some indication of whether non-null was "on" in the assembly.

Genel TürlerGenerics

Bir tür parametresinde T null değer atanamaz kısıtlamalar varsa, kapsamı içinde null atanamaz olarak değerlendirilir.If a type parameter T has non-nullable constraints, it is treated as non-nullable within its scope.

Bir tür parametresi kısıtlanmamışsa veya yalnızca null yapılabilir kısıtlamalara sahipse, bu durum biraz daha karmaşıktır: Bu, karşılık gelen tür bağımsız değişkeninin Nullable veya null yapılamayan olabileceği anlamına gelir.If a type parameter is unconstrained or has only nullable constraints, the situation is a little more complex: this means that the corresponding type argument could be either nullable or non-nullable. Bu durumda yapılacak güvenli şey, tür parametresini hem null yapılabilir hem de null değer atanabilir olarak değerlendirmek, her iki durumda da uyarı vermek olur.The safe thing to do in that situation is to treat the type parameter as both nullable and non-nullable, giving warnings when either is violated.

Açık null yapılabilir başvuru kısıtlamalarına izin verilip verilmeyeceğini göz önünde bulundurmayı düşünün.It is worth considering whether explicit nullable reference constraints should be allowed. Ancak, null yapılabilir başvuru türlerinin belirli durumlarda (devralınan kısıtlamalar) örtük olarak kısıtlamalar olmasını önlamadığımızda unutmayın.Note, however, that we cannot avoid having nullable reference types implicitly be constraints in certain cases (inherited constraints).

classKısıtlama null değil.The class constraint is non-null. class?"Nullable başvuru türü" belirten geçerli bir null yapılabilir kısıtlama olup olmayacağını düşünebiliriz.We can consider whether class? should be a valid nullable constraint denoting "nullable reference type".

Tür çıkarımıType inference

Tür çıkarımı içinde, katkıda bulunan bir tür null yapılabilir bir başvuru türü ise, sonuçta elde edilen tür null yapılabilir olmalıdır.In type inference, if a contributing type is a nullable reference type, the resulting type should be nullable. Diğer bir deyişle, nulllılık yayılır.In other words, nullness is propagated.

nullBir katılım ifadesi olarak sabit değerinin boş değer içerip içermediğini düşünmemiz gerekir.We should consider whether the null literal as a participating expression should contribute nullness. Bugün değil: bir hataya neden olan değer türleri için, ancak başvuru türleri için null değeri, düz türe başarıyla dönüştürülür.It doesn't today: for value types it leads to an error, whereas for reference types the null successfully converts to the plain type.

string? n = "world";
var x = b ? "Hello" : n; // string?
var y = b ? "Hello" : null; // string? or error
var z = b ? 7 : null; // Error today, could be int?

Null Guard KılavuzuNull guard guidance

Özellik olarak, null yapılabilir başvuru türleri geliştiricilerin amacını ifade etmelerine izin verir ve bu amaç çelişmişse akış analizi aracılığıyla uyarılar sağlar.As a feature, nullable reference types allow developers to express their intent, and provide warnings through flow analysis if that intent is contradicted. Null korumalara gerek olup olmadığı konusunda genel bir soru vardır.There is a common question as to whether or not null guards are necessary.

Null Guard örneğiExample of null guard

public void DoWork(Worker worker)
{
    // Guard against worker being null
    if (worker is null)
    {
        throw new ArgumentNullException(nameof(worker));
    }

    // Otherwise use worker argument
}

Önceki örnekte, DoWork işlevi, Worker büyük olasılıkla bu potansiyel olarak bir ve koruyucuları kabul eder null .In the previous example, the DoWork function accepts a Worker and guards against it potentially being null. workerBağımsız değişken ise null , DoWork işlevi olur throw .If the worker argument is null, the DoWork function will throw. Null yapılabilir başvuru türleriyle, önceki örnekteki kod Worker parametrenin olmaması amacını vermez null .With nullable reference types, the code in the previous example makes the intent that the Worker parameter would not be null. DoWorkİşlev bir NuGet paketi veya paylaşılan kitaplık gibi ortak BIR API ise, null koruyucuları yerinde bırakmanız gerekir.If the DoWork function was a public API, such as a NuGet package or a shared library - as guidance you should leave null guards in place. Ortak bir API olarak, bir çağıranın null geçirmediği tek garanti buna karşı koruma içindir.As a public API, the only guarantee that a caller isn't passing null is to guard against it.

Hızlı amaçExpress intent

Önceki örneğin daha etkileyici bir kullanımı, parametrenin olduğunu ifade etmek ve Worker null null koruyucuyu daha uygun hale getirmek olacaktır.A more compelling use of the previous example is to express that the Worker parameter could be null, thus making the null guard more appropriate. Aşağıdaki örnekte null koruyucuyu kaldırırsanız, derleyici null olarak başvuru çıkardıysanız uyarır.If you remove the null guard in the following example, the compiler warns that you may be dereferencing null. Ne olursa olsun, her iki null koruma hala geçerlidir.Regardless, both null guards are still valid.

public void DoWork(Worker? worker)
{
    // Guard against worker being null
    if (worker is null)
    {
        throw new ArgumentNullException(nameof(worker));
    }

    // Otherwise use worker argument
}

Kaynak kodu bir geliştirici veya dev ekibi tarafından yalnızca denetimde olan genel olmayan API 'Ler için-Nullable başvuru türleri, geliştiricilerin gerekli olmadığı garantide garanti edebilecekleri null koruyucuları güvenli bir şekilde kaldırılmasına izin verebilir.For non-public APIs, such as source code entirely in control by a developer or dev team - the nullable reference types could allow for the safe removal of null guards where the developers can guarantee it is not necessary. Özelliği uyarılarla yardımcı olabilir, ancak çalışma zamanı kodu yürütmesinin bir ile sonuçlanabileceğini garanti edemez NullReferenceException .The feature can help with warnings, but it cannot guarantee that at runtime code execution could result in a NullReferenceException.

Yeni değişikliklerBreaking changes

Null olmayan uyarılar, mevcut kodda belirgin bir büyük değişiklik ve bir katılım mekanizması ile birlikte kullanılmalıdır.Non-null warnings are an obvious breaking change on existing code, and should be accompanied with an opt-in mechanism.

Daha az açıktır, null yapılabilir türlerden uyarılar (yukarıda açıklandığı gibi), null değer alabilme 'in örtük olduğu belirli senaryolarda, var olan kodda önemli bir değişiklik olur:Less obviously, warnings from nullable types (as described above) are a breaking change on existing code in certain scenarios where the nullability is implicit:

  • Kısıtlanmış olmayan tür parametreleri örtük olarak null yapılabilir olarak değerlendirilir, bu nedenle bunlara atama object ya da erişim gibi, ToString uyarı verecek.Unconstrained type parameters will be treated as implicitly nullable, so assigning them to object or accessing e.g. ToString will yield warnings.
  • Tür çıkarımı null deyimlerden boş değer içeriyorsa, var olan kod bazen null yapılamayan türler yerine null yapılabilir hale gelir, bu da yeni uyarılara yol açabilir.if type inference infers nullness from null expressions, then existing code will sometimes yield nullable rather than non-nullable types, which can lead to new warnings.

Bu nedenle null yapılabilir uyarıların da isteğe bağlı olması gerekirSo nullable warnings also need to be optional

Son olarak, var olan bir API 'ye ek açıklamalar eklemek, kitaplığı yükselttiğinizde uyarıları kabul eden kullanıcılara bir son değişiklik olacaktır.Finally, adding annotations to an existing API will be a breaking change to users who have opted in to warnings, when they upgrade the library. Bu, çok, kabul etme veya kapatma imkanını de sağlar. "Hata düzeltmelerini istiyorum, ancak yeni ek açıklamalarıyla uğraşmak için hazırlanma"This, too, merits the ability to opt in or out. "I want the bug fixes, but I am not ready to deal with their new annotations"

Özet bölümünde şunları kabul etmeniz gerekir:In summary, you need to be able to opt in/out of:

  • Null yapılabilir uyarılarNullable warnings
  • Null olmayan uyarılarNon-null warnings
  • Diğer dosyalardaki ek açıklamaların uyarılarıWarnings from annotations in other files

Kabul etme özelliğinin ayrıntı düzeyi, swaths of Code 'un pragmalar ile kabul eteceği ve önem düzeyleri Kullanıcı tarafından seçilebilir şekilde, çözümleyici benzeri bir model önerir.The granularity of the opt-in suggests an analyzer-like model, where swaths of code can opt in and out with pragmas and severity levels can be chosen by the user. Ayrıca, kitaplık başına seçenekler ("sonlanmaya hazır olana kadar JSON.NET ek açıklamalarını yoksayın"), kodda öznitelik olarak ifade edilebilir.Additionally, per-library options ("ignore the annotations from JSON.NET until I'm ready to deal with the fall out") may be expressible in code as attributes.

Katılım/geçiş deneyiminin tasarımı, bu özelliğin başarısı ve yararlılığı açısından önemlidir.The design of the opt-in/transition experience is crucial to the success and usefulness of this feature. Şunları yaptığınızdan emin olmak istiyoruz:We need to make sure that:

  • Kullanıcılar, null olabilme denetlemesiniUsers can adopt nullability checking gradually as they want to
  • Kitaplık yazarları, son müşterilerin korku olmadan null değer alabilirlik ek açıklamaları ekleyebilirLibrary authors can add nullability annotations without fear of breaking customers
  • Bunlara rağmen, "yapılandırmanızın gecemi" hakkında bir fikir yokturDespite these, there is not a sense of "configuration nightmare"

AtabileceğiTweaks

?Yereller üzerinde ek açıklamaları kullanmaktan hangilerinin, bunlara atanan neye uygun olarak kullanılıp kullanılmayacağını gözlemliyoruz.We could consider not using the ? annotations on locals, but just observing whether they are used in accordance with what gets assigned to them. Bunu tercih ediyorum; İnsanların amacını hızlı bir şekilde sunalım etmem gerektiğini düşündük.I don't favor this; I think we should uniformly let people express their intent.

T! xÇalışma zamanı null denetimini otomatik olarak üreten parametrelere ilişkin bir toplu değer düşünün.We could consider a shorthand T! x on parameters, that auto-generates a runtime null check.

Ya da gibi genel türlerde bazı desenler, FirstOrDefault TryGet null olamayan tür bağımsız değişkenleriyle biraz tuhaf davranışa sahiptir, çünkü açıkça belirli durumlarda varsayılan değerler verir.Certain patterns on generic types, such as FirstOrDefault or TryGet, have slightly weird behavior with non-nullable type arguments, because they explicitly yield default values in certain situations. Tür sistemini bu daha iyi uyum sağlayacak şekilde kullanmayı deneyebiliriz.We could try to nuance the type system to accommodate these better. Örneğin, ? tür bağımsız değişkeni zaten null yapılabilir olmasına rağmen kısıtlanmış tür parametrelerine izin vereceğiz.For instance, we could allow ? on unconstrained type parameters, even though the type argument could already be nullable. Ona değdiğini biliyorum ve null yapılabilir değer türleriyle etkileşim ile ilgili olarak bir sorun doğurur.I doubt that it is worth it, and it leads to weirdness related to interaction with nullable value types.

Boş değer atanabilen değer türleriNullable value types

Null yapılabilir değer türleri için yukarıdaki semantiklerinden bazılarını benimseme de düşüneceğiz.We could consider adopting some of the above semantics for nullable value types as well.

int? (7, null) Yalnızca bir hata vermek yerine, içinden çıkarsanımız tür çıkarımı zaten belirtiliyor.We already mentioned type inference, where we could infer int? from (7, null), instead of just giving an error.

Farklı bir fırsat, akış analizinin null yapılabilir değer türlerine uygulanmasına olanak tanır.Another opportunity is to apply the flow analysis to nullable value types. Null olmayan kabul edildiğinde, gerçekte null yapılamayan tür olarak kullanılmasına izin veririz (örn. üye erişimi).When they are deemed non-null, we could actually allow using as the non-nullable type in certain ways (e.g. member access). Yalnızca null yapılabilen bir değer türü üzerinde zaten yapabileceğiniz şeylerin, geriye doğru uyumluluk nedenleriyle tercih edildiğini dikkatli etmemiz gerekir.We just have to be careful that the things that you can already do on a nullable value type will be preferred, for back compat reasons.