Tandai parameter sebagai TIDAK nullable di C # /. NET?

99

Apakah ada atribut sederhana atau kontrak data yang dapat saya tetapkan ke parameter fungsi yang mencegah nullagar tidak diteruskan di C # /. NET? Idealnya ini juga akan memeriksa pada waktu kompilasi untuk memastikan literal nulltidak digunakan di mana pun untuk itu dan saat run-time throw ArgumentNullException.

Saat ini saya menulis sesuatu seperti ...

if (null == arg)
  throw new ArgumentNullException("arg");

... untuk setiap argumen yang saya harapkan tidak terjadi null.

Pada catatan yang sama, apakah ada kebalikan dari Nullable<>hal berikut ini yang akan gagal:

NonNullable<string> s = null; // throw some kind of exception
Neil C. Obremski
sumber
7
Dengan versi C # terbaru, penggunaan "nameof ()" bekerja lebih baik: lempar ArgumentNullException baru (nameof (arg)); Dengan begitu, jika Anda refactor nama, jika riak ke pernyataan lemparan Anda
Flydog57
C # 8 sekarang mendukung jenis referensi nullable yang merupakan opsi kompilator yang dapat Anda aktifkan dalam proyek Anda. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Paul Stegler

Jawaban:

69

Sayangnya, tidak ada yang tersedia pada waktu kompilasi.

Saya memiliki sedikit solusi hacky yang saya posting di blog saya baru-baru ini, yang menggunakan struct dan konversi baru.

Di .NET 4.0 dengan hal-hal Kontrak Kode , hidup akan jauh lebih menyenangkan. Akan lebih baik jika memiliki sintaks bahasa aktual dan dukungan seputar non-nullability, tetapi kontrak kode akan banyak membantu.

Saya juga memiliki metode ekstensi di MiscUtil yang disebut ThrowIfNull yang membuatnya sedikit lebih sederhana.

Satu poin terakhir - ada alasan untuk menggunakan " if (null == arg)" daripada " if (arg == null)"? Saya menemukan yang terakhir lebih mudah dibaca, dan masalah yang diselesaikan sebelumnya di C tidak berlaku untuk C #.

Jon Skeet
sumber
2
> Sayangnya, tidak ada yang tersedia pada waktu kompilasi. > ...> Akan lebih baik jika memiliki sintaks bahasa aktual dan dukungan seputar non-nullability. Saya setuju. Saya juga ingin melihat kesalahan waktu kompilasi dimunculkan.
AndrewJacksonZA
2
@ Jon, bukankah "if (arg = null)" berfungsi jika kebetulan ada cast implisit ke bool yang ditentukan? Saya akui ini mungkin tampak sesat, tetapi itu mengkompilasi ...
Thomas S. Trias
11
@ ThomasS.Trias: Ya, dalam kasus tepi yang sangat tidak jelas itu, dikombinasikan dengan kesalahan ketik, dikombinasikan dengan kurangnya tes di sekitar kode itu, Anda akan berakhir dengan masalah. Pada titik itu saya pikir Anda memiliki masalah yang lebih besar :)
Jon Skeet
4
@ Jon: Diberikan. Saya kira saya akan menyerahkan kondisi Yoda saya tercinta. :-)
Thomas S. Trias
1
@SebastianMach: Saya tidak percaya alasannya if (null == x)adalah karena perbedaan bahasa yang alami. Saya percaya ini benar-benar tentang gaya usang yang hanya menyebar melalui contoh, dll.
Jon Skeet
22

Saya tahu saya sangat terlambat untuk pertanyaan ini, tetapi saya merasa jawabannya akan menjadi relevan karena iterasi utama terbaru dari C # mendekati rilis, kemudian dirilis. Dalam C # 8.0 perubahan besar akan terjadi, C # akan menganggap semua jenis dianggap tidak null.

Menurut Mads Torgersen:

Masalahnya adalah bahwa referensi nol sangat berguna. Di C #, mereka adalah nilai default dari setiap tipe referensi. Apa lagi nilai defaultnya? Nilai lain apa yang akan dimiliki variabel, sampai Anda dapat memutuskan apa lagi yang akan diberikan padanya? Nilai apa lagi yang bisa kita buat untuk melengkapi array referensi yang baru saja dialokasikan, sampai Anda sempat mengisinya?

Juga, terkadang null adalah nilai yang masuk akal. Terkadang Anda ingin merepresentasikan fakta bahwa, katakanlah, sebuah bidang tidak memiliki nilai. Tidak masalah untuk meneruskan "nothing" untuk sebuah parameter. Namun, penekanannya kadang-kadang. Dan di sinilah letak bagian lain dari masalah: Bahasa seperti C # tidak membiarkan Anda mengungkapkan apakah null di sini adalah ide yang bagus atau tidak.

Jadi resolusi yang digariskan oleh Mads, adalah:

  1. Kami percaya bahwa lebih umum menginginkan referensi tidak nol. Jenis referensi yang tidak dapat dibatalkan akan menjadi jenis yang lebih langka (meskipun kami tidak memiliki data yang baik untuk memberi tahu kami seberapa banyak), jadi merekalah yang harus memerlukan anotasi baru.

  2. Bahasa ini sudah memiliki pengertian - dan sintaks untuk - tipe nilai nullable. Analogi antara keduanya akan membuat penjumlahan bahasa lebih mudah secara konseptual, dan lebih sederhana secara linguistik.

  3. Tampaknya benar bahwa Anda tidak boleh membebani diri sendiri atau konsumen Anda dengan nilai nol yang tidak praktis kecuali Anda secara aktif memutuskan bahwa Anda menginginkannya. Null, bukan ketiadaan, harus menjadi hal yang harus Anda pilih secara eksplisit.

Contoh fitur yang diinginkan:

public class Person
{
     public string Name { get; set; } // Not Null
     public string? Address { get; set; } // May be Null
}

Pratinjau tersedia untuk Visual Studio 2017, 15.5.4+ pratinjau.

Greg
sumber
1
Adakah yang tahu jika ini pernah menjadi bagian dari C # 8.0?
TroySteven
@TroySteven Ya, Anda harus memilihnya melalui pengaturan di Visual Studio.
Greg
@TroySteven Berikut adalah dokumentasinya. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Greg
Bagus, jadi sepertinya Anda harus mengaktifkannya sebelum mulai berfungsi, itu bagus untuk kompatibilitas ke belakang.
TroySteven
15

Saya tahu ini adalah pertanyaan yang SANGAT lama, tetapi yang ini hilang di sini:

Jika Anda menggunakan ReSharper / Rider, Anda dapat menggunakan Kerangka Beranotasi .

Sunting : Saya baru saja mendapat -1 acak untuk jawaban ini. Tidak apa-apa. Sadarilah bahwa ini masih valid, meskipun itu bukan lagi pendekatan yang direkomendasikan untuk proyek C # 8.0 + (untuk memahami mengapa, lihat jawaban Greg ).

rsenna
sumber
1
Menariknya, bahwa secara default aplikasi yang Anda kompilasi tidak akan memiliki referensi ke JetBrains.Annotations.dll sehingga Anda tidak perlu mendistribusikannya dengan aplikasi: Cara menggunakan JetBrains Annotations untuk meningkatkan inspeksi ReSharper
Lu55
9

Lihat validator di perpustakaan perusahaan. Anda dapat melakukan sesuatu seperti:

private MyType _someVariable = TenantType.None;
[NotNullValidator(MessageTemplate = "Some Variable can not be empty")]
public MyType SomeVariable {
    get {
        return _someVariable;
    }
    set {
        _someVariable = value;
    }
}

Kemudian di kode Anda ketika Anda ingin memvalidasinya:

Microsoft.Practices.EnterpriseLibrary.Validation.Validator myValidator = ValidationFactory.CreateValidator<MyClass>();

ValidationResults vrInfo = InternalValidator.Validate(myObject);
Bukan saya
sumber
0

bukan yang tercantik tapi:

public static bool ContainsNullParameters(object[] methodParams)
{
     return (from o in methodParams where o == null).Count() > 0;
}

Anda juga bisa menjadi lebih kreatif dalam metode ContainsNullParameters:

public static bool ContainsNullParameters(Dictionary<string, object> methodParams, out ArgumentNullException containsNullParameters)
       {
            var nullParams = from o in methodParams
                             where o.Value == null
                             select o;

            bool paramsNull = nullParams.Count() > 0;


            if (paramsNull)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in nullParams)
                    sb.Append(param.Key + " is null. ");

                containsNullParameters = new ArgumentNullException(sb.ToString());
            }
            else
                containsNullParameters = null;

            return paramsNull;
        }

tentu saja Anda bisa menggunakan interseptor atau refleksi tetapi ini mudah diikuti / digunakan dengan sedikit overhead

Jeff Grizzle
sumber
3
Praktik yang sangat buruk menggunakan kueri seperti itu: return (from o in methodParams where o == null).Count() > 0; Gunakan: return methodParams.Any(o=>o==null);akan jauh lebih cepat di koleksi besar
Yavanosta
Mengapa menyebarkan pengecualian? Mengapa tidak dibuang saja?
Martin Capodici
-4

Oke, balasan ini agak terlambat, tetapi inilah cara saya menyelesaikannya:

public static string Default(this string x)
{
    return x ?? "";
}

Gunakan metode ekstensi ini maka Anda dapat memperlakukan string kosong dan null sebagai hal yang sama.

Misalnya

if (model.Day.Default() == "")
{
    //.. Do something to handle no Day ..
}

Tidak ideal saya tahu karena Anda harus ingat untuk memanggil default di mana-mana tetapi ini adalah salah satu solusi.

Martin Capodici
sumber
5
bagaimana ini bisa lebih baik / lebih mudah daripada pemeriksaan (x == null)? atau fungsi String.IsNotNullOrEmpty.
Batavia
Tidak jauh lebih baik daripada string.IsNotNullOrEmptybenar-benar selain gula memiliki hal yang Anda pedulikan di sebelah kiri. Dapat dimasukkan ke dalam fungsi lain (panjang, rangkaian) dll. Sedikit lebih baik.
Martin Capodici
@MartinCapodici Selain itu, ini menciptakan ilusi bahwa Anda aman untuk memanggil metode instance ( Default()) pada null ( model.Day). Saya tahu metode ekstensi tidak diperiksa terhadap null, tetapi mata saya tidak sadar dan mereka sudah membayangkannya ?ke dalam kode: model?.Day?.Default():)
Alb