Tutorial: Mengekspresikan niat desain Anda dengan lebih jelas dengan jenis referensi nullable dan non-nullable
Jenis referensi nullable melengkapi jenis referensi dengan cara yang sama jenis nilai nullable melengkapi jenis nilai. Anda mendeklarasikan variabel menjadi jenis referensi yang dapat diubah ke null dengan menambahkan ?
ke jenis. Misalnya, string?
mewakili dapat diubah ke null string
. Anda dapat menggunakan jenis baru ini untuk mengekspresikan niat desain Anda dengan lebih jelas: beberapa variabel harus selalu memiliki nilai, yang lain mungkin kehilangan nilai.
Dalam tutorial ini, Anda akan mempelajari cara:
- Menggabungkan jenis referensi nullable dan non-nullable ke dalam desain Anda
- Aktifkan pemeriksaan jenis referensi yang dapat diubah ke null di seluruh kode Anda.
- Tulis kode tempat pengompilasi memberlakukan keputusan desain tersebut.
- Gunakan fitur referensi yang dapat diubah ke null dalam desain Anda sendiri
Prasyarat
Anda harus menyiapkan komputer untuk menjalankan .NET, termasuk pengkompilasi C#. Pengkompilasi C# tersedia dengan Visual Studio 2022, atau .NET SDK.
Tutorial ini mengasumsikan Anda terbiasa dengan C# dan .NET, termasuk Visual Studio atau .NET CLI.
Menggabungkan jenis referensi nullable dan non-nullable ke dalam desain Anda
Dalam tutorial ini, Anda akan membuat pustaka yang memodelkan menjalankan survei. Kode ini menggunakan jenis referensi nullable dan jenis referensi yang tidak dapat diubah ke null untuk mewakili konsep dunia nyata. Pertanyaan survei tidak pernah bisa berstatus null. Responden mungkin lebih suka tidak menjawab pertanyaan. Responsnya mungkin null
dalam kasus ini.
Kode yang akan Anda tulis untuk sampel ini mengekspresikan niat tersebut, dan pengompilasi memberlakukan niat tersebut.
Membuat aplikasi dan mengaktifkan jenis referensi yang dapat diubah ke null
Buat aplikasi konsol baru baik di Visual Studio atau dari baris perintah menggunakan dotnet new console
. Beri nama aplikasi NullableIntroduction
. Setelah membuat aplikasi, Anda harus menentukan bahwa seluruh proyek dikompilasi dalam konteks anotasi nullable yang diaktifkan. Buka file .csproj dan tambahkan elemen Nullable
ke elemen PropertyGroup
. Atur nilainya menjadi enable
. Anda harus memilih fitur jenis referensi yang dapat diubah ke null dalam proyek yang lebih lama dari C# 11. Itu karena setelah fitur diaktifkan, deklarasi variabel referensi yang ada menjadi jenis referensi yang tidak dapat diubah ke null. Meskipun keputusan tersebut akan membantu menemukan masalah di mana kode yang ada mungkin tidak memiliki pemeriksaan null yang tepat, hal tersebut mungkin tidak secara akurat mencerminkan niat desain asli Anda:
<Nullable>enable</Nullable>
Sebelum .NET 6, proyek baru tidak menyertakan Nullable
elemen. Dimulai dengan .NET 6, proyek baru menyertakan elemen <Nullable>enable</Nullable>
dalam file proyek.
Merancang jenis untuk aplikasi
Aplikasi survei ini memerlukan pembuatan sejumlah kelas:
- Kelas yang memodelkan daftar pertanyaan.
- Kelas yang memodelkan daftar orang yang dihubungi untuk survei.
- Kelas yang memodelkan jawaban dari orang yang mengikuti survei.
Jenis-jenis ini akan menggunakan jenis referensi nullable dan non-nullable untuk mengekspresikan anggota mana yang diperlukan dan anggota mana yang opsional. Jenis referensi yang dapat diubah ke null mengkomunikasikan niat desain tersebut dengan jelas:
- Pertanyaan yang merupakan bagian dari survei tidak akan bisa berstatus null: Tidak mungkin mengajukan pertanyaan kosong.
- Responden tidak akan bisa berstatus null. Anda mungkin ingin melacak orang yang Anda hubungi, bahkan responden yang menolak berpartisipasi.
- Setiap respons terhadap pertanyaan dapat berstatus null. Responden dapat menolak menjawab beberapa atau semua pertanyaan.
Jika Anda telah memprogram di C#, Anda mungkin sangat terbiasa dengan jenis referensi yang memungkinkan null
nilai yang mungkin melewatkan peluang lain untuk mendeklarasikan instans yang tidak dapat diubah ke null:
- Kumpulan pertanyaan harus tidak dapat diubah ke null.
- Pengumpulan responden harus tidak dapat diubah ke null.
Saat Anda menulis kode, Anda akan melihat bahwa jenis referensi yang tidak dapat diubah ke null sebagai default untuk referensi akan menghindari kesalahan umum yang dapat menyebabkan NullReferenceExceptiond. Salah satu pelajaran dari tutorial ini adalah Anda membuat keputusan tentang variabel mana yang bisa atau tidak bisa menjadi null
. Bahasa komputer tidak menyediakan sintaks untuk mengekspresikan keputusan tersebut. Sekarang, tidak lagi.
Aplikasi yang akan Anda buat melakukan langkah-langkah berikut:
- Membuat survei dan menambahkan pertanyaan ke dalamnya.
- Membuat sekumpulan responden pseudo-random untuk survei.
- Menghubungi responden hingga ukuran survei yang selesai mencapai nomor tujuan.
- Menulis statistik penting tentang respons survei.
Membangun survei dengan jenis referensi nullable dan non-nullable
Kode pertama yang akan Anda tulis membuat survei. Anda akan menulis kelas untuk memodelkan pertanyaan survei dan survei yang dijalankan. Survei Anda memiliki tiga jenis pertanyaan, dibedakan dengan format jawaban: jawaban Ya/Tidak, jawaban angka, dan jawaban teks. Membuat kelas public SurveyQuestion
:
namespace NullableIntroduction
{
public class SurveyQuestion
{
}
}
Pengompilasi menginterpretasikan setiap deklarasi variabel jenis referensi sebagai jenis referensi yang tidak dapat diubah ke null untuk kode dalam konteks anotasi null yang diaktifkan. Anda dapat melihat peringatan pertama Anda dengan menambahkan properti untuk teks pertanyaan dan jenis pertanyaan, seperti yang ditunjukkan dalam kode berikut:
namespace NullableIntroduction
{
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
}
}
Karena Anda belum menginisialisasi QuestionText
, pengompilasi mengeluarkan peringatan bahwa properti yang tidak dapat diubah ke null belum diinisialisasi. Desain Anda mengharuskan teks pertanyaan menjadi non-null, sehingga Anda menambahkan konstruktor untuk menginisialisasinya beserta QuestionType
nilainya. Definisi kelas yang sudah selesai terlihat seperti kode berikut:
namespace NullableIntroduction;
public enum QuestionType
{
YesNo,
Number,
Text
}
public class SurveyQuestion
{
public string QuestionText { get; }
public QuestionType TypeOfQuestion { get; }
public SurveyQuestion(QuestionType typeOfQuestion, string text) =>
(TypeOfQuestion, QuestionText) = (typeOfQuestion, text);
}
Menambahkan konstruktor akan menghapus peringatan. Argumen konstruktor juga merupakan jenis referensi yang tidak dapat diubah ke null, sehingga pengompilasi tidak mengeluarkan peringatan apa pun.
Selanjutnya, buat public
kelas bernama SurveyRun
. Kelas ini berisi daftar SurveyQuestion
objek dan metode untuk menambahkan pertanyaan ke survei, seperti yang ditunjukkan dalam kode berikut:
using System.Collections.Generic;
namespace NullableIntroduction
{
public class SurveyRun
{
private List<SurveyQuestion> surveyQuestions = new List<SurveyQuestion>();
public void AddQuestion(QuestionType type, string question) =>
AddQuestion(new SurveyQuestion(type, question));
public void AddQuestion(SurveyQuestion surveyQuestion) => surveyQuestions.Add(surveyQuestion);
}
}
Seperti sebelumnya, Anda harus menginisialisasi objek daftar ke nilai non-null atau pengompilasi akan mengeluarkan peringatan. Tidak ada pemeriksaan null dalam kelebihan beban AddQuestion
yang kedua karena tidak diperlukan: Anda telah menyatakan variabel tersebut tidak dapat diubah ke null. Nilainya tidak bisa null
.
Beralih ke Program.cs di editor Anda dan ganti konten Main
dengan baris kode berikut:
var surveyRun = new SurveyRun();
surveyRun.AddQuestion(QuestionType.YesNo, "Has your code ever thrown a NullReferenceException?");
surveyRun.AddQuestion(new SurveyQuestion(QuestionType.Number, "How many times (to the nearest 100) has that happened?"));
surveyRun.AddQuestion(QuestionType.Text, "What is your favorite color?");
Karena seluruh proyek berada dalam konteks anotasi nullable yang diaktifkan, Anda akan mendapatkan peringatan saat Anda meneruskan null
ke metode apa pun yang mengharapkan jenis referensi yang tidak dapat diubah ke null. Coba tambahkan baris berikut ke Main
:
surveyRun.AddQuestion(QuestionType.Text, default);
Membuat responden dan mendapatkan jawaban atas survei
Selanjutnya, tulis kode yang menghasilkan jawaban atas survei. Proses ini melibatkan beberapa tugas kecil:
- Buat metode yang menghasilkan objek responden. Ini mewakili orang-orang yang diminta untuk mengisi survei.
- Buat logika untuk mensimulasikan mengajukan pertanyaan kepada responden dan mengumpulkan jawaban atau mencatat bahwa responden tidak menjawab.
- Ulangi hingga responden yang cukup telah menjawab survei.
Anda memerlukan kelas untuk mewakili respons survei, jadi tambahkan sekarang. Aktifkan dukungan yang dapat diubah ke null. Tambahkan Id
properti dan konstruktor yang menginisialisasinya, seperti yang diperlihatkan dalam kode berikut:
namespace NullableIntroduction
{
public class SurveyResponse
{
public int Id { get; }
public SurveyResponse(int id) => Id = id;
}
}
Selanjutnya, tambahkan metode static
untuk membuat peserta baru dengan membuat ID acak:
private static readonly Random randomGenerator = new Random();
public static SurveyResponse GetRandomId() => new SurveyResponse(randomGenerator.Next());
Tanggung jawab utama kelas ini adalah untuk menghasilkan respons bagi peserta terhadap pertanyaan dalam survei. Tanggung jawab ini memiliki beberapa langkah:
- Meminta partisipasi dalam survei. Jika orang tersebut tidak menyetujui, kembalikan respons yang hilang (atau null).
- Ajukan setiap pertanyaan dan catat jawabannya. Setiap jawaban juga mungkin hilang (atau null).
Tambahkan kode berikut ke kelas SurveyResponse
Anda:
private Dictionary<int, string>? surveyResponses;
public bool AnswerSurvey(IEnumerable<SurveyQuestion> questions)
{
if (ConsentToSurvey())
{
surveyResponses = new Dictionary<int, string>();
int index = 0;
foreach (var question in questions)
{
var answer = GenerateAnswer(question);
if (answer != null)
{
surveyResponses.Add(index, answer);
}
index++;
}
}
return surveyResponses != null;
}
private bool ConsentToSurvey() => randomGenerator.Next(0, 2) == 1;
private string? GenerateAnswer(SurveyQuestion question)
{
switch (question.TypeOfQuestion)
{
case QuestionType.YesNo:
int n = randomGenerator.Next(-1, 2);
return (n == -1) ? default : (n == 0) ? "No" : "Yes";
case QuestionType.Number:
n = randomGenerator.Next(-30, 101);
return (n < 0) ? default : n.ToString();
case QuestionType.Text:
default:
switch (randomGenerator.Next(0, 5))
{
case 0:
return default;
case 1:
return "Red";
case 2:
return "Green";
case 3:
return "Blue";
}
return "Red. No, Green. Wait.. Blue... AAARGGGGGHHH!";
}
}
Penyimpanan untuk jawaban survei adalah Dictionary<int, string>?
, yang menunjukkan bahwa itu mungkin null. Anda menggunakan fitur bahasa baru untuk mendeklarasikan niat desain Anda, baik kepada pengompilasi maupun kepada siapa pun yang membaca kode Anda nanti. Jika Anda pernah melakukan dereferensi surveyResponses
tanpa memeriksa nilai null
terlebih dahulu, Anda akan mendapatkan peringatan pengompilasi. Anda tidak mendapatkan peringatan dalam metode AnswerSurvey
karena pengompilasi dapat menentukan variabel surveyResponses
diatur ke nilai non-null di atas.
Menggunakan null
untuk jawaban yang hilang menyoroti titik kunci untuk bekerja dengan jenis referensi yang dapat diubah ke null: tujuan Anda bukan untuk menghapus semua nilai null
dari program Anda. Sebaliknya, tujuan Anda adalah memastikan bahwa kode yang Anda tulis mengekspresikan niat desain Anda. Nilai yang hilang adalah konsep yang diperlukan untuk diekspresikan dalam kode Anda. null
Nilai adalah cara yang jelas untuk mengekspresikan nilai yang hilang tersebut. Mencoba menghapus semua null
nilai hanya menyebabkan penentuan beberapa cara lain untuk mengekspresikan nilai yang hilang tanpa null
.
Berikutnya, Anda perlu menuliskan PerformSurvey
metode dalam SurveyRun
kelas. Tambahkan kode berikut ke SurveyRun
kelas:
private List<SurveyResponse>? respondents;
public void PerformSurvey(int numberOfRespondents)
{
int respondentsConsenting = 0;
respondents = new List<SurveyResponse>();
while (respondentsConsenting < numberOfRespondents)
{
var respondent = SurveyResponse.GetRandomId();
if (respondent.AnswerSurvey(surveyQuestions))
respondentsConsenting++;
respondents.Add(respondent);
}
}
Di sini, pilihan Anda yang dapat diubah ke null List<SurveyResponse>?
menunjukkan responsnya mungkin null. Itu menunjukkan survei belum diberikan kepada responden mana pun. Perhatikan bahwa responden yang ditambahkan sampai mencukupi telah menyetujui.
Langkah terakhir untuk menjalankan survei adalah menambahkan panggilan untuk melakukan survei di akhir Main
metode:
surveyRun.PerformSurvey(50);
Memeriksa respons survei
Langkah terakhir adalah menampilkan hasil survei. Anda akan menambahkan kode ke banyak kelas yang telah Anda tulis. Kode ini menunjukkan nilai yang membedakan jenis referensi nullable dan non-nullable. Mulailah dengan menambahkan dua anggota berekspresi berikut ke SurveyResponse
kelas:
public bool AnsweredSurvey => surveyResponses != null;
public string Answer(int index) => surveyResponses?.GetValueOrDefault(index) ?? "No answer";
Karena surveyResponses
merupakan jenis referensi yang dapat diubah ke null, pemeriksaan null diperlukan sebelum membatalkan referensinya. Answer
Metode mengembalikan string yang tidak dapat diubah ke null, jadi kita harus membahas kasus jawaban yang hilang dengan menggunakan operator null-coalescing.
Selanjutnya, tambahkan ketiga anggota berekspresi ini ke SurveyRun
kelas:
public IEnumerable<SurveyResponse> AllParticipants => (respondents ?? Enumerable.Empty<SurveyResponse>());
public ICollection<SurveyQuestion> Questions => surveyQuestions;
public SurveyQuestion GetQuestion(int index) => surveyQuestions[index];
Anggota AllParticipants
harus mempertimbangkan bahwa variabel respondents
mungkin null, tetapi nilai yang dikembalikan tidak boleh null. Jika Anda mengubah ekspresi tersebut dengan menghapus ??
dan urutan kosong yang mengikuti, pengompilasi memperingatkan Anda bahwa metode mungkin kembali ke null
dan tanda tangan pengembaliannya mengembalikan jenis yang tidak dapat diubah ke null.
Terakhir, tambahkan perulangan berikut di bagian bawah Main
metode:
foreach (var participant in surveyRun.AllParticipants)
{
Console.WriteLine($"Participant: {participant.Id}:");
if (participant.AnsweredSurvey)
{
for (int i = 0; i < surveyRun.Questions.Count; i++)
{
var answer = participant.Answer(i);
Console.WriteLine($"\t{surveyRun.GetQuestion(i).QuestionText} : {answer}");
}
}
else
{
Console.WriteLine("\tNo responses");
}
}
Anda tidak memerlukan pemeriksaan null
dalam kode ini karena Anda telah merancang antarmuka yang mendasar sehingga semuanya mengembalikan jenis referensi yang tidak dapat diubah ke null.
Mendapatkan kode
Anda bisa mendapatkan kode untuk tutorial yang sudah selesai dari repositori sampel kami dalam folder csharp/NullableIntroduction.
Eksperimen dengan mengubah deklarasi jenis antara jenis referensi nullable dan non-nullable. Lihat bagaimana itu menghasilkan peringatan yang berbeda untuk memastikan Anda tidak secara sengaja melakukan dereferensi terhadap null
.
Langkah berikutnya
Pelajari cara menggunakan jenis referensi yang dapat diubah ke null saat menggunakan Kerangka Kerja Entitas: