Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Tutorial ini mengajarkan Anda sejumlah fitur dalam bahasa .NET dan C#. Anda akan mempelajari:
- Dasar-dasar .NET CLI
- Struktur Aplikasi Konsol C#
- Konsol I/O
- Dasar-dasar API I/O File di .NET
- Dasar-dasar Pemrograman Asinkron berbasis Tugas di .NET
Anda akan membuat aplikasi yang membaca file teks, dan menggemakan konten file teks tersebut ke konsol. Output ke konsol diatur kecepatannya agar sesuai dengan membaca dengan keras. Anda dapat mempercepat atau memperlambat kecepatan dengan menekan tombol '<' (kurang dari) atau '>' (lebih besar dari). Anda dapat menjalankan aplikasi ini di Windows, Linux, macOS, atau dalam kontainer Docker.
Ada banyak fitur dalam tutorial ini. Mari kita buat satu per satu.
Prasyarat
- .NET SDK terbaru
- Visual Studio Code editor
- The C# DevKit
Membuat aplikasi
Langkah pertama adalah membuat aplikasi baru. Buka prompt perintah dan buat direktori baru untuk aplikasi Anda. Jadikan itu direktori saat ini. Ketik perintah dotnet new console
pada prompt perintah. Ini membuat file awal untuk aplikasi "Halo Dunia" dasar.
Sebelum Anda mulai membuat modifikasi, mari kita jalankan aplikasi Hello World sederhana. Setelah membuat aplikasi, ketik dotnet run
di prompt perintah. Perintah ini menjalankan proses pemulihan paket NuGet, membuat aplikasi executable, dan menjalankan executable tersebut.
Kode aplikasi Hello World sederhana semuanya dalam Program.cs. Buka file tersebut dengan editor teks favorit Anda. Ganti kode di Program.cs dengan kode berikut:
namespace TeleprompterConsole;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Di bagian atas file, lihat pernyataan namespace
. Seperti bahasa Berorientasi Objek lainnya yang mungkin telah Anda gunakan, C# menggunakan namespace untuk mengatur jenis. Program Halo Dunia ini tidak berbeda. Anda dapat melihat bahwa program berada di namespace dengan nama TeleprompterConsole
.
Membaca dan Menggemakan File
Fitur pertama yang ditambahkan adalah kemampuan untuk membaca file teks dan menampilkan semua teks tersebut ke konsol. Pertama, mari kita tambahkan file teks. Salin file sampleQuotes.txt dari repositori GitHub untuk sampel ini ke direktori proyek Anda. Ini akan berfungsi sebagai skrip untuk aplikasi Anda. Untuk informasi tentang cara mengunduh aplikasi sampel untuk tutorial ini, lihat instruksi dalam Sampel dan Tutorial.
Selanjutnya, tambahkan metode berikut di kelas Program
Anda (tepat di bawah metode Main
):
static IEnumerable<string> ReadFrom(string file)
{
string? line;
using (var reader = File.OpenText(file))
{
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
Metode ini adalah jenis khusus metode C# yang disebut metode iterator . Metode iterator mengembalikan urutan yang dievaluasi secara tunda. Itu berarti setiap item dalam urutan dihasilkan karena diminta oleh kode yang menggunakan urutan. Metode iterator adalah metode yang berisi satu atau beberapa pernyataan yield return
. Objek yang dikembalikan oleh metode ReadFrom
berisi kode untuk menghasilkan setiap item secara berurutan. Dalam contoh ini, yang melibatkan pembacaan baris teks berikutnya dari file sumber, dan mengembalikan string tersebut. Setiap kali kode panggilan meminta item berikutnya dari urutan, kode membaca baris teks berikutnya dari file dan mengembalikannya. Ketika file benar-benar dibaca, urutan menunjukkan bahwa tidak ada lagi item.
Ada dua elemen sintaks C# yang mungkin baru bagi Anda. Pernyataan using
dalam metode ini mengelola pembersihan sumber daya. Variabel yang diinisialisasi dalam pernyataan using
(reader
, dalam contoh ini) harus mengimplementasikan antarmuka IDisposable. Antarmuka tersebut mendefinisikan satu metode, Dispose
, yang harus dipanggil ketika sumber daya harus dirilis. Pengompilasi menghasilkan panggilan itu ketika eksekusi mencapai kurung kurawal penutup dari pernyataan using
. Kode yang dihasilkan kompilator memastikan bahwa sumber daya dirilis bahkan jika terjadi pengecualian dari kode di blok yang ditentukan oleh pernyataan using.
Variabel reader
didefinisikan menggunakan kata kunci var
.
var
mendefinisikan variabel lokal yang ditik secara implisit. Itu berarti jenis variabel ditentukan oleh jenis waktu kompilasi objek yang ditetapkan ke variabel. Di sini, itu adalah nilai pengembalian dari metode OpenText(String), yang merupakan objek StreamReader.
Sekarang, mari kita isi kode untuk membaca file dalam metode Main
:
var lines = ReadFrom("sampleQuotes.txt");
foreach (var line in lines)
{
Console.WriteLine(line);
}
Jalankan program (menggunakan dotnet run
) dan Anda dapat melihat setiap baris yang dicetak ke konsol.
Menambahkan penundaan dan pemformatan output
Apa yang Anda miliki ditampilkan terlalu cepat untuk dibaca dengan suara keras. Sekarang Anda perlu menambahkan penundaan dalam output. Saat memulai, Anda akan membangun beberapa kode inti yang memungkinkan pemrosesan asinkron. Namun, langkah-langkah pertama ini akan mengikuti beberapa anti-pola. Anti-pola ditunjukkan dalam komentar ketika Anda menambahkan kode, dan kode akan diperbarui pada langkah berikutnya.
Ada dua langkah untuk bagian ini. Pertama, Anda akan memperbarui metode iterator untuk mengembalikan satu kata, bukan seluruh baris. Itu dilakukan dengan modifikasi ini. Ganti pernyataan yield return line;
dengan kode berikut:
var words = line.Split(' ');
foreach (var word in words)
{
yield return word + " ";
}
yield return Environment.NewLine;
Selanjutnya, Anda perlu memodifikasi bagaimana Anda memproses baris-baris file, dan mengatur jeda ketika menulis setiap kata. Ganti pernyataan Console.WriteLine(line)
dalam metode Main
dengan blok berikut:
Console.Write(line);
if (!string.IsNullOrWhiteSpace(line))
{
var pause = Task.Delay(200);
// Synchronously waiting on a task is an
// anti-pattern. This will get fixed in later
// steps.
pause.Wait();
}
Jalankan sampel, dan periksa output. Sekarang, setiap kata dicetak, diikuti oleh penundaan 200 ms. Namun, output yang ditampilkan menunjukkan beberapa masalah karena file teks sumber memiliki beberapa baris yang memiliki lebih dari 80 karakter tanpa pemisah baris. Itu bisa sulit dibaca saat sedang menggulir. Itu mudah diperbaiki. Anda hanya akan melacak panjang setiap baris, dan menghasilkan garis baru setiap kali panjang garis mencapai ambang batas tertentu. Deklarasikan variabel lokal setelah deklarasi words
dalam metode ReadFrom
yang menahan panjang baris:
var lineLength = 0;
Kemudian, tambahkan kode berikut setelah pernyataan yield return word + " ";
(sebelum kurung kurawal penutup):
lineLength += word.Length + 1;
if (lineLength > 70)
{
yield return Environment.NewLine;
lineLength = 0;
}
Jalankan sampel, dan Anda akan dapat membaca dengan suara keras pada kecepatan yang dikonfigurasi sebelumnya.
Tugas Asinkron
Dalam langkah terakhir ini, Anda akan menambahkan kode untuk menulis output secara asinkron dalam satu tugas, sambil menjalankan tugas lain untuk membaca input dari pengguna jika mereka ingin mempercepat atau memperlambat tampilan teks, atau menghentikan tampilan teks sama sekali. Ini memiliki beberapa langkah di dalamnya dan pada akhirnya, Anda akan memiliki semua pembaruan yang Anda butuhkan. Langkah pertama adalah membuat metode pengembalian Task asinkron yang mewakili kode yang telah Anda buat sejauh ini untuk membaca dan menampilkan file.
Tambahkan metode ini ke kelas Program
Anda (diambil dari isi metode Main
Anda):
private static async Task ShowTeleprompter()
{
var words = ReadFrom("sampleQuotes.txt");
foreach (var word in words)
{
Console.Write(word);
if (!string.IsNullOrWhiteSpace(word))
{
await Task.Delay(200);
}
}
}
Anda akan melihat dua perubahan. Pertama, dalam isi metode, alih-alih memanggil Wait() untuk secara sinkron menunggu tugas selesai, versi ini menggunakan kata kunci await
. Untuk melakukannya, Anda perlu menambahkan pengubah async
ke tanda tangan metode. Metode ini mengembalikan Task
. Perhatikan bahwa tidak ada pernyataan pengembalian yang mengembalikan objek Task
. Sebagai gantinya, objek Task
tersebut dibuat oleh kode yang dihasilkan kompilator saat Anda menggunakan operator await
. Anda dapat membayangkan bahwa metode ini kembali ketika mencapai await
.
Task
yang dikembalikan menunjukkan bahwa pekerjaan belum selesai. Metode dilanjutkan ketika tugas yang ditunggu selesai. Ketika telah dijalankan hingga selesai, Task
yang dikembalikan menunjukkan bahwa proses selesai.
Kode panggilan dapat memantau hasil pengembalian Task
untuk menentukan kapan proses tersebut selesai.
Tambahkan kata kunci await
sebelum panggilan ke ShowTeleprompter
:
await ShowTeleprompter();
Ini mengharuskan Anda mengubah tanda tangan metode Main
menjadi:
static async Task Main(string[] args)
Pelajari selengkapnya tentang metode async Main
di bagian dasar-dasar kami.
Selanjutnya, Anda perlu menulis metode asinkron kedua untuk membaca dari konsol dan mengawasi tombol '<' (kurang dari), '>' (lebih besar dari) dan tombol 'X' atau 'x'. Berikut adalah metode yang Anda tambahkan untuk tugas tersebut:
private static async Task GetInput()
{
var delay = 200;
Action work = () =>
{
do {
var key = Console.ReadKey(true);
if (key.KeyChar == '>')
{
delay -= 10;
}
else if (key.KeyChar == '<')
{
delay += 10;
}
else if (key.KeyChar == 'X' || key.KeyChar == 'x')
{
break;
}
} while (true);
};
await Task.Run(work);
}
Ini membuat ekspresi lambda untuk mewakili delegasi Action yang membaca kunci dari Konsol dan memodifikasi variabel lokal yang mewakili penundaan saat pengguna menekan tombol '<' (kurang dari) atau '>' (lebih besar dari). Metode delegasi selesai ketika pengguna menekan tombol 'X' atau 'x', yang memungkinkan pengguna menghentikan tampilan teks kapan saja. Metode ini menggunakan ReadKey() untuk memblokir dan menunggu pengguna menekan tombol.
Untuk menyelesaikan fitur ini, Anda perlu membuat metode pengembalian async Task
baru yang memulai kedua tugas ini (GetInput
dan ShowTeleprompter
), dan juga mengelola data bersama antara kedua tugas ini.
Saatnya membuat kelas yang dapat menangani data bersama di antara kedua tugas ini. Kelas ini berisi dua properti publik: penundaan, dan bendera Done
untuk menunjukkan bahwa file telah benar-benar dibaca:
namespace TeleprompterConsole;
internal class TelePrompterConfig
{
public int DelayInMilliseconds { get; private set; } = 200;
public void UpdateDelay(int increment) // negative to speed up
{
var newDelay = Min(DelayInMilliseconds + increment, 1000);
newDelay = Max(newDelay, 20);
DelayInMilliseconds = newDelay;
}
public bool Done { get; private set; }
public void SetDone()
{
Done = true;
}
}
Masukkan kelas tersebut ke dalam file baru, dan sertakan kelas tersebut di namespace TeleprompterConsole
seperti yang ditunjukkan. Anda juga perlu menambahkan pernyataan using static
di bagian atas file sehingga Anda dapat merujuk metode Min
dan Max
tanpa nama kelas atau namespace yang menyertakannya. Pernyataan using static
mengimpor metode dari satu kelas. Ini berbeda dengan pernyataan using
tanpa static
, yang mengimpor semua kelas dari namespace.
using static System.Math;
Selanjutnya, Anda perlu memperbarui metode ShowTeleprompter
dan GetInput
untuk menggunakan objek config
baru. Tulis satu metode final Task
yang mengembalikan async
untuk memulai kedua tugas dan keluar saat tugas pertama selesai.
private static async Task RunTeleprompter()
{
var config = new TelePrompterConfig();
var displayTask = ShowTeleprompter(config);
var speedTask = GetInput(config);
await Task.WhenAny(displayTask, speedTask);
}
Satu metode baru di sini adalah panggilan WhenAny(Task[]). Itu membuat Task
yang selesai segera setelah salah satu tugas dalam daftar argumennya selesai.
Selanjutnya, Anda perlu memperbarui metode ShowTeleprompter
dan GetInput
untuk menggunakan objek config
untuk penundaan:
private static async Task ShowTeleprompter(TelePrompterConfig config)
{
var words = ReadFrom("sampleQuotes.txt");
foreach (var word in words)
{
Console.Write(word);
if (!string.IsNullOrWhiteSpace(word))
{
await Task.Delay(config.DelayInMilliseconds);
}
}
config.SetDone();
}
private static async Task GetInput(TelePrompterConfig config)
{
Action work = () =>
{
do {
var key = Console.ReadKey(true);
if (key.KeyChar == '>')
config.UpdateDelay(-10);
else if (key.KeyChar == '<')
config.UpdateDelay(10);
else if (key.KeyChar == 'X' || key.KeyChar == 'x')
config.SetDone();
} while (!config.Done);
};
await Task.Run(work);
}
Versi baru ShowTeleprompter
ini memanggil metode baru di kelas TeleprompterConfig
. Sekarang, Anda perlu memperbarui Main
untuk memanggil RunTeleprompter
alih-alih ShowTeleprompter
:
await RunTeleprompter();
Kesimpulan
Tutorial ini menunjukkan kepada Anda sejumlah fitur di sekitar bahasa C# dan pustaka .NET Core yang terkait dengan bekerja di aplikasi Konsol. Anda dapat membangun pengetahuan ini untuk menjelajahi lebih lanjut tentang bahasa, dan kelas yang diperkenalkan di sini. Anda telah melihat dasar-dasar I/O File dan Konsol, penggunaan pemrograman asinkron berbasis Tugas baik yang memblokir maupun yang tidak memblokir, tur bahasa C# dan bagaimana program C# diatur, serta CLI .NET.
Untuk informasi selengkapnya tentang File I/O, lihat File dan Stream I/O. Untuk informasi selengkapnya tentang model pemrograman asinkron yang digunakan dalam tutorial ini, lihat Pemrograman Asinkron berbasis Tugas dan pemrograman asinkron .