Dapatkah saya memberi tahu C # nullable referensi bahwa suatu metode secara efektif merupakan pemeriksaan nol pada suatu bidang

14

Pertimbangkan kode berikut:

#nullable enable
class Foo
{
    public string? Name { get; set; }
    public bool HasName => Name != null;
    public void NameToUpperCase()
    {
        if (HasName)
        {
            Name = Name.ToUpper();
        }
    }
}

Pada Name = Name.ToUpper () saya mendapat peringatan bahwa Name adalah referensi nol, yang jelas salah. Saya bisa menyembuhkan peringatan ini dengan memasukkan HasName sehingga kondisinya adalah jika (Nama! = Null).

Apakah ada cara saya bisa menginstruksikan kompiler bahwa tanggapan sebenarnya dari HasName menyiratkan kendala non-nullability pada Nama?

Ini penting karena HasName mungkin sebenarnya menguji lebih banyak hal, dan saya mungkin ingin menggunakannya di beberapa tempat, atau mungkin menjadi bagian publik dari permukaan API. Ada banyak alasan untuk ingin memasukkan cek nol ke dalam metode itu sendiri, tetapi hal itu tampaknya mematahkan pemeriksa referensi nullable.

John Melville
sumber
1
IMO yang harus Anda gunakan HasValuepada jenis yang dapat dibatalkan, bukan mengeceknya null. Ini mungkin tidak mempengaruhi masalah Anda.
fredrik
Saya pikir untuk kasus Anda, Anda dapat membungkus kode Anda dengan #nullable disableitu #nullable enableatau restorelagi sesudahnya ( docs.microsoft.com/en-us/dotnet/csharp/… ).
GaryNg
5
Anda bisa menggunakan !operator "sialan" . if(HasName) { Name = Name!.ToUpper(); }
Jan Paolo Go
1
untuk aplikasi multi-utas, Anda bisa membuat Nama menjadi nol setelah cek HasName, menggunakan variabel secara lokal alih-alih kembali ke properti (siapa yang tahu apa yang mungkin dilakukan properti dalam pengambilannya) akan memberikan beberapa bug yang funky (ingat penggunaan event handler di mana ini banyak terjadi)
XIU

Jawaban:

3

Saya melihat-lihat atribut yang berbeda dari System.Diagnostics.CodeAnalysisdan saya tidak dapat menemukan sesuatu yang berlaku, yang sangat mengecewakan. Tampaknya yang paling dekat dengan apa yang Anda inginkan adalah:

public bool TryGetName([NotNullWhen(true)] out string? name)
{
    name = Name;
    return name != null;
}

public void NameToUpperCase()
{
    if (TryGetName(out var name))
    {
        Name = name.ToUpper();
    }
}

Kelihatannya cukup rumit, saya tahu. Anda dapat melihat pada dokumen MSDN untuk atribut yang dapat dibatalkan , mungkin Anda akan menemukan sesuatu yang lebih rapi.

V0ldek
sumber
2
Sepertinya kita membutuhkan lebih banyak atribut atau sesuatu seperti pernyataan naskah
Stilgar
Saya akan memilih yang ini sebagai jawabannya, karena tampaknya jawaban yang sebenarnya, seperti yang saya khawatirkan, adalah "tidak, c # belum melakukan itu."
John Melville
@JohnMelville Saya juga tidak dapat menemukan proposal untuk fitur seperti itu, jadi saya tidak berpikir kita dapat mengharapkan perubahan ini dalam waktu dekat.
V0ldek
2
@XIU Kompiler sudah lemah dalam aspek ini. Jika Anda melakukannya if(Name != null) return Null.ToUpper(), tidak akan ada peringatan untuk dereferensi nol, meskipun secara teknis itu adalah kondisi lomba TOCTOU. Saya ingat Mads Torgersen berbicara tentang bagaimana mereka menganggap itu, tetapi itu akan menghasilkan begitu banyak kesalahan positif sehingga seluruh fitur tipe referensi yang dapat dibatalkan akan sia-sia - 99% dari waktu properti Anda tidak akan diubah oleh utas lainnya. Jadi yang perlu Anda lakukan adalah membuat atribut yang akan membuat pemeriksaan pada properti ini diperlakukan sebagai cek untuk null pada properti lain.
V0ldek
2
Saya memperbaiki masalah "tidak dapat menemukan proposal untuk ini". ( github.com/dotnet/csharplang/issues/2997 ) Semoga saya beruntung.
John Melville
-10

String adalah tipe referensi, dan nullable (misalnya int?) adalah tipe nilai nullable. Jadi Anda tidak bisa melakukan ini string? myString; Yang Anda butuhkan adalah ini:

class Foo
{
    public string Name { get; set; }
    public bool HasName => !String.IsNullOrEmpty(Name);  ////assume you want empty to be treated same way as null
    public void NameToUpperCase()
    {
        if (HasName)
        {
            Name = Name.ToUpper();
        }
    }
}
daxu
sumber