次の方法で共有


チュートリアル: ファイル ベースの C# プログラムをビルドする

ファイル ベースのアプリは、対応するプロジェクト (*.cs) ファイルなしでビルドおよび実行される 1 つの*.csproj ファイル内に含まれるプログラムです。 ファイルベースのアプリは、複雑さが少ないため、C# の学習に最適です。プログラム全体が 1 つのファイルに格納されます。 ファイル ベースのアプリは、コマンド ライン ユーティリティの構築にも役立ちます。 Unix プラットフォームでは、ファイルベースのアプリは #! (shebang) ディレクティブを使用して実行できます。 このチュートリアルでは、次の操作を行います。

  • ファイル ベースのプログラムを作成します。
  • Unix shebang (#!) のサポートを追加します。
  • コマンド ライン引数を読み取ります。
  • 標準入力を処理します。
  • ASCII アート出力を書き込みます。
  • コマンド ライン引数を処理します。
  • 解析されたコマンド ラインの結果を使用します。
  • 最終的なアプリケーションをテストします。

テキストを ASCII アートとして書き込むファイル ベースのプログラムを作成します。 アプリは 1 つのファイルに含まれていて、コア機能の一部を実装する NuGet パッケージを使用します。

[前提条件]

ファイル ベースのプログラムを作成する

  1. Visual Studio Code を開き、 AsciiArt.csという名前の新しいファイルを作成します。 次のテキストを入力します。

    Console.WriteLine("Hello, world!");
    
  2. ファイルを保存します。 次に、Visual Studio Code で統合ターミナルを開き、次のように入力します。

    dotnet run AsciiArt.cs
    

このプログラムを初めて実行すると、 dotnet ホストはソース ファイルから実行可能ファイルをビルドし、ビルド成果物を一時フォルダーに格納してから、作成された実行可能ファイルを実行します。 このエクスペリエンスを確認するには、もう一度「 dotnet run AsciiArt.cs 」と入力します。 今回、 dotnet ホストは実行可能ファイルが最新であると判断し、再度ビルドせずに実行可能ファイルを実行します。 ビルド出力が表示されません。

上記の手順では、ファイル ベースのアプリがスクリプト ファイルでないことを示します。 これらは、一時フォルダー内の生成されたプロジェクト ファイルを使用して構築された C# ソース ファイルです。 プログラムのビルド時に表示される出力行の 1 つは、次のようになります (Windows の場合)。

AsciiArt succeeded (7.3s) → AppData\Local\Temp\dotnet\runfile\AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc\bin\debug\AsciiArt.dll

unix プラットフォームでは、出力フォルダーは次のようになります。

AsciiArt succeeded (7.3s) → Library/Application Support/dotnet/runfile/AsciiArt-85c58ae0cd68371711f06f297fa0d7891d0de82afde04d8c64d5f910ddc04ddc/bin/debug/AsciiArt.dll

その出力は、一時ファイルとビルド出力が配置される場所を示します。 このチュートリアルでは、ソース ファイルを編集するたびに、 dotnet ホストは実行可能ファイルを実行する前に更新します。

ファイル ベースのアプリは、通常の C# プログラムです。 唯一の制限は、1 つのソース ファイルに書き込む必要があるということです。 最上位レベルのステートメントまたはクラシック Main メソッドをエントリ ポイントとして使用できます。 クラス、インターフェイス、構造体など、任意の型を宣言できます。 ファイル ベースのプログラム内のアルゴリズムは、任意の C# プログラムと同じように構成できます。 複数の名前空間を宣言してコードを整理することもできます。 ファイル ベースのプログラムが 1 つのファイルに対して大きくなりすぎる場合は、プロジェクト ベースのプログラムに変換し、ソースを複数のファイルに分割できます。 ファイルベースのアプリは、優れたプロトタイプ作成ツールです。 最小限のオーバーヘッドで実験を開始して、概念を証明し、アルゴリズムを構築できます。

Unix shebang (#!) のサポート

#! ディレクティブのサポートは unix プラットフォームでのみ適用されます。 Windows で C# プログラムを直接実行するための同様のディレクティブはありません。 Windows では、コマンド ラインで dotnet run を使用する必要があります。

unix では、ファイル ベースのアプリを直接実行し、 dotnet runの代わりにコマンド ラインにソース ファイル名を入力できます。 次の 2 つの変更を行う必要があります。

  1. ソース ファイルに 対する実行 アクセス許可を設定します。

    chmod +x AsciiArt.cs
    
  2. #! ファイルの最初の行として shebang (AsciiArt.cs) ディレクティブを追加します。

    #!/usr/local/share/dotnet/dotnet run
    

dotnetの場所は、unix のインストールによって異なる場合があります。 コマンド which dotnet を使用して、環境内の dotnet ホストを見つけます。

または、 #!/usr/bin/env dotnet を使用して、PATH 環境変数から dotnet パスを自動的に解決することもできます。

#!/usr/bin/env dotnet

これら 2 つの変更を行った後、コマンド ラインから直接プログラムを実行できます。

./AsciiArt.cs

必要に応じて、拡張機能を削除して、代わりに「 ./AsciiArt 」と入力できます。 Windows を使用している場合でも、ソース ファイルに #! を追加できます。 Windows コマンド ラインは #!をサポートしていませんが、C# コンパイラでは、すべてのプラットフォーム上のファイル ベースのアプリでそのディレクティブを使用できます。

コマンド ライン引数の読み取り

次に、コマンド ラインのすべての引数を出力に書き込みます。

  1. AsciiArt.csの現在の内容を次のコードに置き換えます。

    if (args.Length > 0)
    {
        string message = string.Join(' ', args);
        Console.WriteLine(message);
    }
    
  2. このバージョンは、次のコマンドを入力して実行できます。

    dotnet run AsciiArt.cs -- This is the command line.
    

    -- オプションは、次のすべてのコマンド引数を AsciiArt プログラムに渡す必要があることを示します。 This is the command line.引数は文字列の配列として渡されます。各文字列は、Thisisthecommand、およびline.の 1 つの単語です。

このバージョンでは、次の新しい概念を示します。

  • コマンド ライン引数は、定義済みの変数 argsを使用してプログラムに渡されます。 args変数は文字列の配列です:string[]argsの長さが 0 の場合は、引数が指定されなかったことを意味します。 それ以外の場合、引数リストの各単語は、配列内の対応するエントリに格納されます。
  • string.Join メソッドは、指定された区切り記号を使用して、複数の文字列を 1 つの文字列に結合します。 この場合、区切り記号は 1 つのスペースです。
  • Console.WriteLine は、文字列を標準出力コンソールに書き込み、その後に新しい行を書き込みます。

標準入力を処理する

これは、コマンド ライン引数を正しく処理します。 次に、コマンド ライン引数ではなく、標準入力 (stdin) からの入力の読み取りを処理するコードを追加します。

  1. 前のコードで追加したelse ステートメントに、次のif句を追加します。

    else
    {
        while (Console.ReadLine() is string line && line.Length > 0)
        {
            Console.WriteLine(line);
        }
    }
    

    上記のコードは、空白行または null が読み取られるまでコンソール入力を読み取ります。 (Console.ReadLine メソッドは、null キーを押して入力ストリームが閉じている場合にを返します)。

  2. 同じフォルダーに新しいテキスト ファイルを作成して、標準入力の読み取りをテストします。 ファイルに input.txt 名前を付け、次の行を追加します。

    Hello from ...
    dotnet!
    
    You can create
    file-based apps
    in .NET 10 and
    C# 14
    
    Have fun writing
    useful utilities
    

    ASCII アートを使用する機能を追加するときに、線を短くして正しく書式設定します。

  3. プログラムをもう一度実行します。

    bash の場合:

    cat input.txt | dotnet run AsciiArt.cs
    

    または、PowerShell を使用します。

    Get-Content input.txt | dotnet run AsciiArt.cs
    

これで、プログラムはコマンド ライン引数または標準入力を受け入れることができるようになりました。

ASCII アートの出力を書き込む

次に、ASCII アート である Colorful.Console をサポートするパッケージを追加します。 ファイル ベースのプログラムにパッケージを追加するには、 #:package ディレクティブを使用します。

  1. AsciiArt.cs ファイルの #! ディレクティブの後に次のディレクティブを追加します。

    #:package Colorful.Console@1.2.15
    

    Important

    1.2.15バージョンは、このチュートリアルが最後に更新されたときのColorful.Console パッケージの最新バージョンでした。 パッケージの NuGet ページ で最新バージョンを確認し、最新のセキュリティ修正プログラムでパッケージ バージョンを使用していることを確認します。

  2. 代わりに Console.WriteLine メソッドを使用するようにColorful.Console.WriteAsciiを呼び出す行を変更します。

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  3. プログラムを実行すると、エコーテキストではなくASCIIアート出力が表示されます。

プロセス コマンド オプション

次に、コマンド ライン解析を追加しましょう。 現在のバージョンでは、各単語が異なる出力行として書き込まれます。 追加したコマンド ライン引数は、次の 2 つの機能をサポートします。

  1. 1 行に記述する必要がある複数の単語を引用符で囲む:

    AsciiArt.cs "This is line one" "This is another line" "This is the last line"
    
  2. 各行の間で一時停止する --delay オプションを追加します。

    AsciiArt.cs --delay 1000
    

ユーザーは両方の引数を一緒に使用できる必要があります。

ほとんどのコマンド ライン アプリケーションでは、オプション、コマンド、およびユーザー入力を効果的に処理するために、コマンド ライン引数を解析する必要があります。 System.CommandLine ライブラリには、コマンド、サブコマンド、オプション、および引数を処理するための包括的な機能が用意されており、コマンド ライン入力の解析の仕組みではなく、アプリケーションの動作に集中できます。

System.CommandLine ライブラリには、いくつかの主な利点があります。

  • ヘルプ テキストの自動生成と検証。
  • POSIX および Windows コマンド ライン規則のサポート。
  • 組み込みのタブ補完機能。
  • アプリケーション間での一貫した解析動作。
  1. System.CommandLine パッケージを追加します。 既存のパッケージ ディレクティブの後に次のディレクティブを追加します。

    #:package System.CommandLine@2.0.0
    

    Important

    2.0.0のバージョンは、このチュートリアルが最後に更新されたときの最新バージョンでした。 使用可能な新しいバージョンがある場合は、最新バージョンを使用して、最新のセキュリティ パッケージがあることを確認します。 パッケージの NuGet ページ で最新バージョンを確認し、最新のセキュリティ修正プログラムでパッケージ バージョンを使用していることを確認します。

  2. ファイルの先頭に必要な using ステートメントを追加します ( #! ディレクティブと #:package ディレクティブの後)。

    using System.CommandLine;
    using System.CommandLine.Parsing;
    
  3. 遅延オプションとメッセージ引数を定義します。 コマンド ライン オプションと引数を表す CommandLine.Option オブジェクトと CommandLine.Argument オブジェクトを作成する次のコードを追加します。

    Option<int> delayOption = new("--delay")
    {
        Description = "Delay between lines, specified as milliseconds.",
        DefaultValueFactory = parseResult => 100
    };
    
    Argument<string[]> messagesArgument = new("Messages")
    {
        Description = "Text to render."
    };
    

    コマンド ライン アプリケーションでは、オプションは通常、 -- (二重ダッシュ) で始まり、引数を受け取ることができます。 --delay オプションは、ミリ秒単位の遅延を指定する整数引数を受け取ります。 messagesArgumentでは、オプションの後に残っているトークンをテキストとして解析する方法を定義します。 各トークンは配列内で個別の文字列になりますが、テキストを引用符で囲み、1 つのトークンに複数の単語を含めることができます。 たとえば、 "This is one message" は 1 つのトークンになり、 This is four tokens は 4 つの個別のトークンになります。

    上記のコードでは、 --delay オプションの引数の型を定義し、引数が string 値の配列であることを定義します。 このアプリケーションにはコマンドが 1 つしかないため、 root コマンドを使用します。

  4. ルート コマンドを作成し、オプションと引数を使用して構成します。 引数とオプションをルート コマンドに追加します。

    RootCommand rootCommand = new("Ascii Art file-based program sample");
    
    rootCommand.Options.Add(delayOption);
    rootCommand.Arguments.Add(messagesArgument);
    
  5. コマンド ライン引数を解析し、エラーを処理するコードを追加します。 次のコードは、コマンド ライン引数を検証し、解析された引数を System.CommandLine.ParseResult オブジェクトに格納します。

    ParseResult result = rootCommand.Parse(args);
    foreach (ParseError parseError in result.Errors)
    {
        Console.Error.WriteLine(parseError.Message);
    }
    if (result.Errors.Count > 0)
    {
        return 1;
    }
    

上記のコードは、すべてのコマンド ライン引数を検証します。 検証に失敗すると、エラーがコンソールに書き込まれ、アプリが終了します。

解析されたコマンド ラインの結果を使用する

次に、解析されたオプションを使用するようにアプリを終了し、出力を書き込みます。 最初に、解析されたオプションを保持するレコードを定義します。 ファイル ベースのアプリには、レコードやクラスなどの型宣言を含めることができます。 これらは、すべての最上位レベルのステートメントとローカル関数の後にある必要があります。

  1. メッセージと遅延オプションの値を格納する record 宣言を追加します。

    public record AsciiMessageOptions(string[] Messages, int Delay);
    
  2. レコード宣言の前に、次のローカル関数を追加します。 このメソッドは、コマンド ライン引数と標準入力の両方を処理し、新しいレコード インスタンスを返します。

    async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result)
    {
        int delay = result.GetValue(delayOption);
        List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()];
    
        if (messages.Count == 0)
        {
            while (Console.ReadLine() is string line && line.Length > 0)
            {
                Colorful.Console.WriteAscii(line);
                await Task.Delay(delay);
            }
        }
        return new([.. messages], delay);
    }
    
  3. 指定した遅延で ASCII アートを書き込むローカル関数を作成します。 この関数は、レコード内の各メッセージを、各メッセージ間の指定された遅延で書き込みます。

    async Task WriteAsciiArt(AsciiMessageOptions options)
    {
        foreach (string message in options.Messages)
        {
            Colorful.Console.WriteAscii(message);
            await Task.Delay(options.Delay);
        }
    }
    
  4. 前に記述した if 句を、コマンド ライン引数を処理して出力を書き込む次のコードに置き換えます。

    var parsedArgs = await ProcessParseResults(result);
    
    await WriteAsciiArt(parsedArgs);
    return 0;
    

解析されたコマンド ライン のオプションと引数に構造を提供する record 型を作成しました。 新しいローカル関数は、レコードのインスタンスを作成し、そのレコードを使用して ASCII アート出力を書き込みます。

最終的なアプリケーションをテストする

複数の異なるコマンドを実行して、アプリケーションをテストします。 問題が発生した場合は、ビルドしたサンプルと比較するために完成したサンプルを次に示します。

#!/usr/local/share/dotnet/dotnet run

#:package Colorful.Console@1.2.15
#:package System.CommandLine@2.0.0

using System.CommandLine;
using System.CommandLine.Parsing;

Option<int> delayOption = new("--delay")
{
    Description = "Delay between lines, specified as milliseconds.",
    DefaultValueFactory = parseResult => 100
};

Argument<string[]> messagesArgument = new("Messages")
{
    Description = "Text to render."
};

RootCommand rootCommand = new("Ascii Art file-based program sample");

rootCommand.Options.Add(delayOption);
rootCommand.Arguments.Add(messagesArgument);

ParseResult result = rootCommand.Parse(args);
foreach (ParseError parseError in result.Errors)
{
    Console.Error.WriteLine(parseError.Message);
}
if (result.Errors.Count > 0)
{
    return 1;
}

var parsedArgs = await ProcessParseResults(result);

await WriteAsciiArt(parsedArgs);
return 0;

async Task<AsciiMessageOptions> ProcessParseResults(ParseResult result)
{
    int delay = result.GetValue(delayOption);
    List<string> messages = [.. result.GetValue(messagesArgument) ?? Array.Empty<string>()];

    if (messages.Count == 0)
    {
        while (Console.ReadLine() is string line && line.Length > 0)
        {
            // <WriteAscii>
            Colorful.Console.WriteAscii(line);
            // </WriteAscii>
            await Task.Delay(delay);
        }
    }
    return new([.. messages], delay);
}

async Task WriteAsciiArt(AsciiMessageOptions options)
{
    foreach (string message in options.Messages)
    {
        Colorful.Console.WriteAscii(message);
        await Task.Delay(options.Delay);
    }
}

public record AsciiMessageOptions(string[] Messages, int Delay);

このチュートリアルでは、1 つの C# ファイルでプログラムをビルドするファイル ベースのプログラムを構築する方法について説明しました。 これらのプログラムはプロジェクト ファイルを使用せず、unix システムでは #! ディレクティブを使用できます。 学習者は、 オンラインチュートリアル を試した後、より大きなプロジェクトベースのアプリを構築する前に、これらのプログラムを作成できます。 ファイル ベースのアプリは、コマンド ライン ユーティリティ用の優れたプラットフォームでもあります。