Konsol uygulaması
Bu öğretici, .NET ve C# dilinde birçok özellik öğretir. Şunları öğreneceksiniz:
- .NET CLı 'nin temelleri
- C# konsol uygulamasının yapısı
- Konsol g/ç
- .NET 'teki dosya g/ç API 'Lerinin temelleri
- .NET ' te görev tabanlı zaman uyumsuz programlama temelleri
Bir metin dosyasını okuyan ve bu metin dosyasının içeriğini konsola yansıtan bir uygulama oluşturacaksınız. Konsola giden çıkış, okumayı yüksek sesle eşleştirmeye yönelik olarak ilerleyebileceğiniz bir adım adım olur. ' < ' (küçüktür) veya ' > ' (büyüktür) anahtarlarına basarak hızla hızlandıramaz veya azaltabilirsiniz. bu uygulamayı Windows, Linux, macos veya docker kapsayıcısında çalıştırabilirsiniz.
Bu öğreticide birçok özellik vardır. Bunları birer birer oluşturalım.
Önkoşullar
- .Net 6 SDK.
- Bir kod Düzenleyicisi.
Uygulama oluşturma
İlk adım yeni bir uygulama oluşturmaktır. Bir komut istemi açın ve uygulamanız için yeni bir dizin oluşturun. Geçerli dizini oluşturun. Komutu dotnet new console komut istemine yazın. Bu, temel bir "Merhaba Dünya" uygulaması için başlangıç dosyalarını oluşturur.
Değişiklik yapmaya başlamadan önce basit Merhaba Dünya uygulamasını çalıştıralim. Uygulamayı oluşturduktan sonra dotnet run komut istemine yazın. bu komut NuGet paketi geri yükleme işlemini çalıştırır, uygulama yürütülebilirini oluşturur ve çalıştırılabilir dosyayı çalıştırır.
Basit Merhaba Dünya uygulama kodu, program. cs' de kullanılır. Bu dosyayı en sevdiğiniz metin düzenleyicinizle açın. Program. cs dosyasındaki kodu aşağıdaki kodla değiştirin:
namespace TeleprompterConsole;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Dosyanın üst kısmında, bkz namespace . bir ifade. Kullandığınız diğer nesne yönelimli diller gibi, C#, türleri düzenlemek için ad alanlarını kullanır. Bu Merhaba Dünya program farklı değildir. Programın adında ad alanında olduğunu görebilirsiniz TeleprompterConsole .
Dosyayı okuma ve Yankılandırın
Eklenecek ilk özellik, bir metin dosyasını okuma ve bu metnin tümünü konsola görüntüleme olanağıdır. İlk olarak bir metin dosyası ekleyelim. bu örnek için GitHub deposundan sampleQuotes.txt dosyasını proje dizininize kopyalayın. Bu, uygulamanız için komut dosyası olarak görev yapar. Bu öğreticide örnek uygulamayı indirme hakkında daha fazla bilgi için örnekler ve öğreticiler' daki yönergelere bakın.
Sonra, sınıfınıza aşağıdaki yöntemi ekleyin Program (yöntemin altına doğru Main ):
static IEnumerable<string> ReadFrom(string file)
{
string? line;
using (var reader = File.OpenText(file))
{
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
Bu yöntem, yineleyici yöntemi olarak adlandırılan özel bir C# yöntemi türüdür. Numaralandırıcı yöntemleri değerlendirilen dizileri döndürür geç. Bu, dizideki her öğe, diziyi kullanan kod tarafından istenerek oluşturulduğu anlamına gelir. Numaralandırıcı yöntemleri bir veya daha fazla deyim içeren yöntemlerdir yield return . Yöntemi tarafından döndürülen nesne, ReadFrom dizideki her bir öğeyi oluşturmak için kodu içerir. Bu örnekte, kaynak dosyadaki bir sonraki metin satırını okumayı ve bu dizeyi döndürmeyle ilgilidir. Çağıran kod, sıradaki bir sonraki öğeyi her istediğinde, kod dosyadaki metnin bir sonraki satırını okur ve döndürür. Dosya tamamen okunmadığında, dizi daha fazla öğe olmadığını gösterir.
Sizin için yeni olabilecek iki C# sözdizimi öğesi vardır. usingBu yöntemdeki deyimin kaynağı temizleme işlemini yönetir. İfadesinde başlatılan değişken using ( reader Bu örnekte) IDisposable arabirimini uygulamalıdır. Bu arabirim, Dispose kaynak yayımlanacaksa çağrılması gereken tek bir yöntemini tanımlar. , Yürütme deyimin kapanış ayracına ulaştığında derleyici bu çağrıyı oluşturur using . Derleyici tarafından oluşturulan kod, using ifadesiyle tanımlanan bloktaki koddan bir özel durum oluşsa bile kaynağın serbest bırakılacağını sağlar.
readerDeğişkeni, anahtar sözcüğü kullanılarak tanımlanır var . varörtük olarak yazılmış bir yerel değişken tanımlar. Bu, değişkenin türü değişkene atanan nesnenin derleme zamanı türü tarafından belirlendiği anlamına gelir. Burada, OpenText(String) bir nesnesi olan yönteminden döndürülen değer StreamReader .
Şimdi, yöntemi içindeki dosyayı okumak için kodu dolduralım Main :
var lines = ReadFrom("sampleQuotes.txt");
foreach (var line in lines)
{
Console.WriteLine(line);
}
Programı çalıştırın (kullanarak dotnet run ) ve konsolda yazdırılan her satırı görebilirsiniz.
Gecikme ve biçimlendirme çıktısı ekleme
Ne kadar hızlı görüntülendikleriniz çok yüksek. Şimdi çıktıdaki gecikmeleri eklemeniz gerekir. Başladığınızda, zaman uyumsuz işleme sağlayan çekirdek koddan bazılarını oluşturacaksınız. Bununla birlikte, bu ilk adımlar birkaç tane kenar deseninden daha izlenecek. Siz kodu eklerken yorumlara desenler görüntülenir ve kod sonraki adımlarda güncelleştirilir.
Bu bölümde iki adım vardır. İlk olarak, yineleyici yöntemini tüm satırlar yerine tek bir sözcük döndürecek şekilde güncelleştireceksiniz. Bu değişiklikler bu değişikliklerle yapılır. yield return line;İfadesini aşağıdaki kodla değiştirin:
var words = line.Split(' ');
foreach (var word in words)
{
yield return word + " ";
}
yield return Environment.NewLine;
Ardından, dosyanın satırlarını nasıl kullanacağınızı değiştirmeniz ve her bir kelime yazıldıktan sonra bir gecikme eklemeniz gerekir. Console.WriteLine(line)Yöntemi içindeki ifadesini Main aşağıdaki bloğa değiştirin:
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();
}
Örneği çalıştırın ve çıktıyı denetleyin. Şimdi, her bir sözcük yazdırılır ve ardından 200 ms gecikme gelir. Ancak, görüntülenen çıktıda bazı sorunlar gösterilmektedir çünkü kaynak metin dosyasında satır sonu olmadan 80 ' ten fazla karakter içeren birkaç satır vardır. Bu, kaydırma sırasında okunması zor olabilir. Bu, düzeltilmesi kolay bir işlemdir. Her satırın uzunluğunu takip edersiniz ve satır uzunluğu belirli bir eşiğe ulaştığında yeni bir satır oluşturacaksınız. wordsSatır uzunluğunu tutan yöntemde öğesinin bildiriminden sonra bir yerel değişken bildirin ReadFrom :
var lineLength = 0;
Sonra, deyimden sonra aşağıdaki kodu ekleyin yield return word + " "; (kapanış ayracından önce):
lineLength += word.Length + 1;
if (lineLength > 70)
{
yield return Environment.NewLine;
lineLength = 0;
}
Örneği çalıştırın, daha önceden yapılandırılmış hızda sesli okuyabileceksiniz.
Zaman uyumsuz görevler
Bu son adımda, çıktıyı zaman uyumsuz olarak bir görevde yazacak şekilde, Ayrıca metin görüntüsünü hızlandırmak veya yavaşlatacak şekilde Kullanıcı girişini okumak için başka bir görevi çalıştırırken, metin görüntülemeyi tamamen durdurmak için kodu ekleyeceksiniz. Bunun içinde ve sonunda birkaç adım vardır, ihtiyacınız olan tüm güncelleştirmelere sahip olacaksınız. İlk adım, Task dosyayı okuyup görüntülemesi için oluşturduğunuz kodu temsil eden zaman uyumsuz bir döndürme yöntemi oluşturmaktır.
Bu yöntemi Program sınıfınıza ekleyin (yönteminizin gövdesinden alınır Main ):
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);
}
}
}
İki değişiklik fark edeceksiniz. İlk olarak, yöntemin gövdesinde, Wait() bir görevin bitmesini zaman uyumlu olarak beklemek yerine, bu sürüm await anahtar sözcüğünü kullanır. Bunu yapmak için, async Yöntem imzasına değiştiricisini eklemeniz gerekir. Bu yöntem bir döndürür Task . Bir nesne döndüren return deyimi olmadığına dikkat edin Task . Bunun yerine, Task işleci kullandığınızda derleyicinin oluşturduğu kodla bu nesne oluşturulur await . Bu yöntemin bir öğesine ulaştığında geri döndüğünü hayal edebilirsiniz await . Döndürülen Task iş tamamlanmadığını gösterir. Yöntemi, beklenen görev tamamlandığında devam eder. Tamamlanmayı yürütüldüğünde, döndürülen Task bunun tamamlandığını gösterir.
Çağıran kod, Task ne zaman tamamlandığını anlamak için döndürülen ' i izleyebilir.
Bu yeni yöntemi, yönteyinizdeki çağırabilirsiniz Main :
ShowTeleprompter().Wait();
Burada, Main içinde kod zaman uyumlu olarak bekler. Mümkün olduğunda zaman await uyumlu olarak beklemek yerine işleci kullanılmalıdır. Ancak konsol uygulamasının yönteminde Main işleci await kullanılamaz. Bu, tüm görevler tamamlanmadan önce uygulamanın çıkışa neden olur.
Not
C# 7.1 veya sonraki bir 7.1 kullanıyorsanız, yöntemiyle konsol uygulamaları async Main oluşturabilirsiniz.
Ardından Konsoldan okumak için ikinci zaman uyumsuz yöntemi yazmanız ve '<' (küçük), '>' (büyüktür) ve 'X' veya 'x' anahtarlarını izlemeniz gerekir. Bu görev için aşağıdaki yöntemi eklersiniz:
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);
}
Bu, Konsoldan bir anahtar okunan bir temsilciyi temsil eden bir lambda ifadesi oluşturur ve kullanıcı Action '<' (küçük) veya '>' (büyüktür) tuşlarına basan gecikmeyi temsil eden yerel bir değişkeni değiştiren bir lambda ifadesi oluşturur. Temsilci yöntemi, kullanıcı 'X' veya 'x' tuşlarına basarak kullanıcının metin görüntülemesini herhangi bir zamanda durdurmasına olanak sağlar. Bu yöntem, ReadKey() kullanıcının bir tuşa basmalarını engellemek ve beklemek için kullanır.
Bu özelliği tamamlamak için, bu görevlerin her ikisini de başlatan ( ve ) ve bu iki görev arasındaki paylaşılan verileri yöneten yeni async Task bir dönüş yöntemi oluşturmanız GetInput ShowTeleprompter gerekir.
Şimdi bu iki görev arasında paylaşılan verileri işleye bir sınıf oluşturabilirsiniz. Bu sınıf iki ortak özellik içerir: gecikme ve dosyanın tamamen Done okundu olduğunu belirten bir bayrak:
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;
}
}
Bu sınıfı yeni bir dosyaya ekleyin ve bu sınıfı gösterildiği gibi ad TeleprompterConsole alanına ekleyin. Ayrıca, kapsayan sınıf veya ad alanı adları olmadan ve yöntemlerine başvurabilirsiniz. using static Min Max Deyimi, using static yöntemleri bir sınıftan içeri aktarıyor. Bu, bir ad alanının using tüm sınıflarını static içeri aktaran deyiminin aksinedir.
using static System.Math;
Ardından, yeni nesneyi kullanmak için ShowTeleprompter GetInput ve yöntemlerini güncelleştirmeniz config gerekir. Her iki görevi Task de başlatmak ve ilk görev tamam olduğunda çıkmak için son bir dönüş yöntemi async yazın:
private static async Task RunTeleprompter()
{
var config = new TelePrompterConfig();
var displayTask = ShowTeleprompter(config);
var speedTask = GetInput(config);
await Task.WhenAny(displayTask, speedTask);
}
Buradaki yeni yöntem WhenAny(Task[]) çağrıdır. Bu, Task bağımsız değişken listesinde yer alan görevlerden herhangi biri tamamlandıktan hemen sonra bir oluşturur.
Ardından, gecikme süresi için nesnesini kullanmak üzere hem hem ShowTeleprompter GetInput de yöntemlerini config güncelleştirmeniz gerekir:
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);
}
Bu yeni sürümü ShowTeleprompter sınıfındaki yeni bir yöntemi TeleprompterConfig çağıran. Şimdi yerine çağrısı yapmak Main için RunTeleprompter güncelleştirmeniz ShowTeleprompter gerekir:
RunTeleprompter().Wait();
Sonuç
Bu öğreticide, Konsol uygulamalarında çalışmayla ilgili C# dili ve .NET Core kitaplıkları ile ilgili bir dizi özellik gösterildi. Dil ve burada tanıtilen sınıflar hakkında daha fazla bilgi için bu bilgileri incelersiniz. Görev tabanlı zaman uyumsuz programlamanın engellenmesi ve engelleyici olmayan kullanımı, C# dili turu ve C# programlarının nasıl düzenleniyor olduğu ve .NET CLI hakkında temel bilgileri gördünüz.
Dosya I/O hakkında daha fazla bilgi için bkz. Dosya ve Akış I/O. Bu öğreticide kullanılan zaman uyumsuz programlama modeli hakkında daha fazla bilgi için bkz. Görev Tabanlı Zaman Uyumsuz Programlama ve Zaman Uyumsuz Programlama.