Null referans mümkün görünmediğinde neden olası dereference null referans uyarısı alıyoruz?


10

HNQ hakkında bu soruyu okuduktan sonra , C # 8'de Null olabilecek Referans Türleri hakkında okumaya devam ettim ve bazı deneyler yaptım.

Birisi "Derleyici hata buldum!" Dediğinde 10 kişiden 9'unun, hatta daha sık olduğunu biliyorum. bu aslında tasarımdan ve kendi yanlış anlamalarından kaynaklanmaktadır. Ve bugün sadece bu özelliğe bakmaya başladığım için, açıkça çok iyi bir anlayışım yok. Bu şekilde, şu koda bakalım:

#nullable enable
class Program
{
    static void Main()
    {
        var s = "";
        var b = s == null; // If you comment this line out, the warning on the line below disappears
        var i = s.Length; // warning CS8602: Dereference of a possibly null reference
    }
}

Yukarıda bağlandığım belgeleri okuduktan sonra, s == nullhattın bana bir uyarı vermesini beklerdim - sonuçta saçıkça geçersizdir, bu yüzdennull geçersizdir mantıklı değildir.

Bunun yerine, bir sonraki satırda bir uyarı alıyorum ve uyarı şöyle diyor:s bir insan için öyle olmadığı açık olsa da, boş bir referansın mümkün .

Daha fazla, uyarı edilir değil biz karşılaştırmak yoksa görüntülenen siçin null.

Bazı Googling yaptım ve tamamen başka bir şeyle ilgili olduğu ortaya çıkan bir GitHub sorununa çarptım , ancak bu süreçte bu davranışla ilgili daha fazla fikir veren bir katılımcıyla bir görüşme yaptım (örn. "Boş denetimler genellikle yararlı bir yoldur derleyiciye bir değişkenin kuralsızlığı hakkındaki önceki çıkarımını sıfırlamasını söylemektir. " ). Ancak bu beni asıl soruya cevapsız bıraktı.

Yeni bir GitHub sorunu oluşturmak ve potansiyel olarak inanılmaz derecede proje katılımcılarına zaman ayırmak yerine, bunu topluma veriyorum.

Bana neler olduğunu ve nedenini açıklar mısınız? Özellikle, s == nullhatta neden hiçbir uyarı üretilmez ve burada CS8602bir nullreferans mümkün görünmediğinde neden var? Bağlantısız GitHub iş parçacığının da önerdiği gibi, nullabilite çıkarımı kurşun geçirmez değilse, nasıl yanlış gidebilir? Bunun bazı örnekleri nelerdir?


Derleyicinin kendisi, bu noktada "s" değişkeninin boş olabileceği davranışı ayarlamış görünmektedir. Her neyse, dizeleri veya nesneleri kullanırsam, bir işlevi çağırmadan önce her zaman bir kontrol olmalıdır. "s? .Length" hile yapmalı ve uyarının kendisi yok olmalıdır.
chg

1
@chg, buna gerek olmamalı ?çünkü sboş bırakılamaz . Null edilemez hale gelmez, çünkü onu karşılaştırmak için yeterince aptaldık null.
Andrew Savinykh

Daha önceki bir soruyu takip ediyordum (özür dilerim, bulamıyorum) bir değerin boş olup olmadığını kontrol ederseniz, derleyici değeri 'ipucu' olarak alır null olabilir , bu bile olsa poz verdi takip edildi durum böyle değil.
stuartd

@stuartd, evet, öyle görünüyor. Şimdi soru şu: bu neden faydalı?
Andrew Savinykh

1
@chg, soru gövdesinde söylediğim şey bu değil mi?
Andrew Savinykh

Yanıtlar:


7

Bu, @stuartd'ın bağlandığı cevabın bir kopyasıdır, bu yüzden burada süper derin ayrıntılara girmeyeceğim. Ancak sorunun kökü, bunun ne bir dil hatası ne de bir derleyici hatası olmasıdır, ancak tam olarak uygulandığı gibi amaçlanan davranıştır. Bir değişkenin null durumunu izleriz. Değişkeni ilk olarak bildirdiğinizde, açıkça null olmayan bir değerle başlattığınız için bu durum NotNull olur. Ancak NotNull'un nereden geldiğini takip etmiyoruz. Örneğin bu, etkin bir şekilde eşdeğer koddur:

#nullable enable
class Program
{
    static void Main()
    {
        M("");
    }
    static void M(string s)
    {
        var b = s == null;
        var i = s.Length; // warning CS8602: Dereference of a possibly null reference
    }
}

Her iki durumda da açıkça sınamak siçin null. Bunu akış analizine girdi olarak alıyoruz, tıpkı Mads'in bu soruya cevap vermesi gibi: https://stackoverflow.com/a/59328672/2672518 . Bu cevapta sonuç, dönüş hakkında bir uyarı almanızdır. Bu durumda, yanıt, olası bir boş başvuruyu iptal ettiğinize dair bir uyarı almanızdır.

Null edilemez hale gelmez, çünkü onu karşılaştırmak için yeterince aptaldık null.

Evet, aslında öyle. Derleyiciye. İnsanlar olarak, bu koda bakabilir ve açık bir şekilde istisna oluşturamayacağını açıkça anlayabiliriz. Ancak derlenebilir akış analizinin derleyicide uygulanma şekli, gerçekleştirilemez. Bu analize, değerin nereden geldiğine bağlı olarak ek durumlar eklediğimiz bazı iyileştirmeleri tartıştık, ancak bunun, büyük bir kazanç değil, uygulamaya büyük bir karmaşıklık kattığına karar verdik, çünkü sadece bu, kullanıcının bir değişkeni a newveya sabit bir değerle başlattığı ve ardındannull yine de olacaktır.


Teşekkür ederim. Bu çoğunlukla diğer soru ile benzerliklere değiniyor, farklılıklara daha çok değinmek istiyorum. Örneğin, neden s == nulluyarı oluşturmuyor?
Andrew Savinykh

Ayrıca şunu fark ettim #nullable enable; string s = "";s = null;derleme ve çalışır (yine de bir uyarı üretir) uygulamanın yararları nelerdir, etkin null ek açıklama bağlamında bir "boş olmayan başvuru" null atanmasına izin verir?
Andrew Savinykh

Mad'in cevabı, "[derleyici] ayrı değişkenlerin durumu arasındaki ilişkiyi izlemiyor" gerçeğine odaklanıyor, bu örnekte ayrı değişkenlerimiz yok, bu yüzden Mad'in bu cevabının geri kalanını uygulamada sorun yaşıyorum.
Andrew Savinykh

Söylemeye gerek yok, ama hatırla, burada eleştirmek için değil, öğrenmek için buradayım. 2001'de ilk çıktığı andan itibaren C # kullanıyorum. Dilde yeni olmasam da, derleyicinin davranış şekli benim için şaşırtıcıydı. Bu sorunun amacı, bu davranışın insanlar için neden yararlı olduğunu ortaya koymaktır.
Andrew Savinykh

Kontrol etmek için geçerli nedenler var s == null. Örneğin, herkese açık bir yöntemdesiniz ve parametre doğrulaması yapmak istiyorsunuz. Ya da, belki de yanlış açıklama eklenmiş bir kütüphane kullanıyorsunuz ve bu hatayı düzeltene kadar, bildirilmeyen yerde null ile uğraşmanız gerekiyor. Her iki durumda da, uyardığımızda kötü bir deneyim olurdu. Atamaya izin vermek için: yerel değişken ek açıklamaları sadece okumak içindir. Çalışma zamanını hiç etkilemezler. Aslında, tüm bu uyarıları tek bir hata koduna koyarız, böylece kod karmaşasını azaltmak istiyorsanız bunları kapatabilirsiniz.
333fred

0

Nullabilite çıkarımı kurşun geçirmez değilse [..] nasıl yanlış gidebilir?

C # 8'in null edilebilir referanslarını hazır olur olmaz mutlu bir şekilde kabul ettim. ReSharper'ın [NotNull] (vb.) Gösterimini kullanmak için kullanıldığımdan, ikisi arasında bazı farklılıklar fark ettim.

C # derleyicisi kandırılabilir, ancak dikkatli olunmasına neden olur (genellikle, her zaman değil).

Gelecek ziyaretçiler için bir referans olarak, bunlar derleyicinin oldukça karışık olduğunu gördüğüm senaryolardır (tüm bu vakaların tasarımdan kaynaklandığını varsayıyorum):

  • Boş bağışlayan boş . Genellikle dereference uyarısından kaçınmak için kullanılır, ancak nesneyi boş bırakılmaz tutar. Ayağını iki ayakkabıda tutmak istiyor gibi görünüyor.
    string s = null!; //No warning

  • Yüzey analizi . ReSharper'ın ( kod ek açıklamasını kullanarak yapar) tersine , C # derleyicisi nullable referanslarını işlemek için tam bir dizi özelliği desteklemez.
    void DoSomethingWith(string? s)
    {    
        ThrowIfNull(s);
        var split = s.Split(' '); //Dereference warning
    }

Bununla birlikte, uyarıdan kurtulan nullabiliteyi kontrol etmek için bazı yapıların kullanılmasına izin verir:

    public static void DoSomethingWith(string? s)
    {
        Debug.Assert(s != null, nameof(s) + " != null");
        var split = s.Split(' ');  //No warning
    }

veya (yine de oldukça havalı) özellikler (hepsini burada bulabilirsiniz ):

    public static bool IsNullOrEmpty([NotNullWhen(false)] string? value)
    {
        ...
    }

  • Hassas kod analizi . Işığa getirdiğiniz şey budur. Derleyici çalışmak için varsayımlar yapmak zorundadır ve bazen sezgisel görünebilirler (en azından insanlar için).
    void DoSomethingWith(string s)
    {    
        var b = s == null;
        var i = s.Length; // Dereference warning
    }

  • Jenerik problemler . Sorular burada ve çok iyi izah burada (paragraf "T ile sorunu?" Eskisi gibi yazı). Jenerikler hem referansları hem de değerleri mutlu etmek zorunda oldukları için karmaşıktır. Temel fark, string?sadece bir dize olmasına rağmen , bir int?olur Nullable<int>ve derleyiciyi bunları büyük ölçüde farklı şekillerde işlemeye zorlar. Ayrıca burada, derleyici güvenli yolu seçiyor ve sizi ne beklediğinizi belirtmeye zorluyor:
    public interface IResult<out T> : IResult
    {
        T? Data { get; } //Warning/Error: A nullable type parameter must be known to be a value type or non-nullable reference type.
    }

Kısıtlamalar vererek çözüldü:

    public interface IResult<out T> : IResult where T : class { T? Data { get; }}
    public interface IResult<T> : IResult where T : struct { T? Data { get; }}

Ancak, kısıtlamaları kullanmazsak ve '?' Verilerden, hala 'varsayılan' anahtar kelimeyi kullanarak null değerler koyabiliriz:

    [Pure]
    public static Result<T> Failure(string description, T data = default)
        => new Result<T>(ResultOutcome.Failure, data, description); 
        // data here is definitely null. No warning though.

Sonuncusu, güvensiz kod yazmasına izin verdiği için benim için daha zor görünüyor.

Umarım bu birine yardımcı olur.


Null olabilecek özelliklere bir doküman vermenizi öneririm : docs.microsoft.com/en-us/dotnet/csharp/nullable-attributes . Sorunlarınızı, özellikle yüzey analizi bölümü ve jenerikler ile çözeceklerdir.
333fred

@ 333fred bağlantı için teşekkürler. Oynayabileceğiniz nitelikler olsa da, gönderdiğim sorunu çözen bir özellik ( ThrowIfNull(s);bana sboş olmadığını söyleyen bir şey yok) mevcut değil. Ayrıca makalede boş değerli olmayan jeneriklerin nasıl işleneceği açıklanırken, derleyiciyi nasıl "kandırabileceğinizi" gösterirken, sıfır değerine sahip ancak bu konuda uyarı yok.
Alvin Sartor

Aslında, özellik mevcut. Eklemek için dokümanlara bir hata gönderdim. Arıyorsunuz DoesNotReturnIf(bool).
333fred

@ 333fred Aslında daha çok benzer bir şey arıyorum DoesNotReturnIfNull(nullable).
Alvin Sartor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.