次の方法で共有


チュートリアル: 学習時に最上位レベルのステートメントを使用してコードをビルドするアイデアを調べる

このチュートリアルでは、次の方法について説明します。

  • 最上位レベルのステートメントの使用を管理する規則について説明します。
  • 最上位レベルのステートメントを使用してアルゴリズムを探索します。
  • 探索を再利用可能なコンポーネントにリファクタリングします。

前提 条件

.NET 6 以降を実行するようにマシンを設定する必要があります。 C# コンパイラは、Visual Studio 2022 以降、または .NET SDK使用できます。

このチュートリアルでは、Visual Studio や .NET CLI など、C# と .NET について理解していることを前提としています。

探索を開始する

トップレベルのステートメントを使用すると、クラス内の静的メソッドにプログラムのエントリ ポイントを置く必要がある余計な儀式的な手順を回避できます。 新しいコンソール アプリケーションの一般的な開始点は、次のコードのようになります。

using System;

namespace Application
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

上記のコードは、dotnet new console コマンドを実行し、新しいコンソール アプリケーションを作成した結果です。 これらの 11 行に含まれる実行可能コードは 1 行のみです。 新しい最上位レベルのステートメント機能を使用すると、そのプログラムを簡略化できます。 これにより、このプログラムの 2 行を除くすべての行を削除できます。

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

重要

.NET 6 用の C# テンプレートでは、最上位レベル ステートメント使用されます。 .NET 6 に既にアップグレードしている場合は、アプリケーションがこの記事のコードと一致しない可能性があります。 詳細については、新しい C# テンプレートで最上位レベルのステートメントを生成する に関する記事を参照してください。

.NET 6 SDK により、次の SDK を使用するプロジェクトに対して暗黙的なglobal usingディレクティブ セットも追加されます。

  • Microsoft.NET.Sdk
  • Microsoft.NET.Sdk.Web
  • Microsoft.NET.Sdk.Worker

これらの暗黙的な global using ディレクティブには、プロジェクトの種類に最も一般的な名前空間が含まれます。

詳細については、「暗黙的な using ディレクティブ」の記事を参照してください

この機能により、新しいアイデアの探索を開始するために必要なものが簡略化されます。 最上位レベルのステートメントは、スクリプト作成シナリオや探索に使用できます。 基本作業が完了したら、コードのリファクタリングを開始し、ビルドした再利用可能なコンポーネントのメソッド、クラス、またはその他のアセンブリを作成できます。 最上位レベルのステートメントを使用すると、簡単な実験と初心者向けのチュートリアルが可能になります。 また、実験から完全なプログラムへのスムーズなパスも提供します。

最上位レベルのステートメントは、ファイルに表示される順序で実行されます。 最上位レベルのステートメントは、アプリケーション内の 1 つのソース ファイルでのみ使用できます。 複数のファイルで使用すると、コンパイラによってエラーが生成されます。

マジック .NET 応答マシンを構築する

このチュートリアルでは、ランダムな回答で "はい" または "いいえ" の質問に回答するコンソール アプリケーションを構築しましょう。 機能を段階的に構築します。 一般的なプログラムの構造で必要とされる形式ばられた手続きではなく、自分のタスクに集中できます。 その後、機能に満足したら、必要に応じてアプリケーションをリファクタリングできます。

良い出発点は、コンソールに質問を書き戻すことです。 まず、次のコードを記述します。

Console.WriteLine(args);

args 変数は宣言しません。 最上位レベルのステートメントを含む単一のソース ファイルの場合、コンパイラはコマンド ライン引数を意味する args を認識します。 引数の型は、すべての C# プログラムと同様に、string[]です。

次の dotnet run コマンドを実行して、コードをテストできます。

dotnet run -- Should I use top level statements in all my programs?

コマンド ラインの -- の後の引数がプログラムに渡されます。 args 変数の型はコンソールに出力されるため、確認できます。

System.String[]

コンソールに質問を書き込むには、引数を列挙し、スペースで区切る必要があります。 WriteLine 呼び出しを次のコードに置き換えます。

Console.WriteLine();
foreach(var s in args)
{
    Console.Write(s);
    Console.Write(' ');
}
Console.WriteLine();

これで、プログラムを実行すると、質問が引数の文字列として正しく表示されます。

ランダムな回答で応答する

質問をエコーした後、ランダムな回答を生成するコードを追加できます。 まず、考えられる回答の配列を追加します。

string[] answers =
[
    "It is certain.",       "Reply hazy, try again.",     "Don’t count on it.",
    "It is decidedly so.",  "Ask again later.",           "My reply is no.",
    "Without a doubt.",     "Better not tell you now.",   "My sources say no.",
    "Yes – definitely.",    "Cannot predict now.",        "Outlook not so good.",
    "You may rely on it.",  "Concentrate and ask again.", "Very doubtful.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
];

この配列には、肯定的な 10 個の回答、非コミット型の 5 個、負の 5 個があります。 次に、次のコードを追加して、配列からランダムな回答を生成して表示します。

var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);

アプリケーションをもう一度実行して結果を確認できます。 次のような出力が表示されます。

dotnet run -- Should I use top level statements in all my programs?

Should I use top level statements in all my programs?
Better not tell you now.

このコードは質問に答えますが、もう 1 つの機能を追加しましょう。 質問アプリで、回答に関する考えをシミュレートしたいと考えています。 これを行うには、少し ASCII アニメーションを追加し、作業中に一時停止します。 質問をエコーする行の後に次のコードを追加します。

for (int i = 0; i < 20; i++)
{
    Console.Write("| -");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("/ \\");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("- |");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("\\ /");
    await Task.Delay(50);
    Console.Write("\b\b\b");
}
Console.WriteLine();

また、ソース ファイルの先頭に using ディレクティブを追加する必要があります。

using System.Threading.Tasks;

using ディレクティブは、ファイル内の他のステートメントの前にする必要があります。 それ以外の場合は、コンパイラ エラーです。 プログラムをもう一度実行し、アニメーションを表示できます。 これにより、エクスペリエンスが向上します。 あなたの好みに合わせて遅延の長さを試してみてください。

上記のコードでは、スペースで区切られた回転する線のセットが作成されます。 await キーワードを追加すると、async 修飾子を持つメソッドとしてプログラム エントリ ポイントを生成し、System.Threading.Tasks.Taskを返すようにコンパイラに指示します。 このプログラムは値を返さないため、プログラムエントリポイントは Taskを返します。 プログラムが整数値を返す場合は、最上位レベルのステートメントの末尾に return ステートメントを追加します。 その return ステートメントは、返す整数値を指定します。 最上位レベルのステートメントに await 式が含まれている場合、戻り値の型は System.Threading.Tasks.Task<TResult>になります。

未来に向けたリファクタリング

プログラムは次のコードのようになります。

Console.WriteLine();
foreach(var s in args)
{
    Console.Write(s);
    Console.Write(' ');
}
Console.WriteLine();

for (int i = 0; i < 20; i++)
{
    Console.Write("| -");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("/ \\");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("- |");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("\\ /");
    await Task.Delay(50);
    Console.Write("\b\b\b");
}
Console.WriteLine();

string[] answers =
[
    "It is certain.",       "Reply hazy, try again.",     "Don't count on it.",
    "It is decidedly so.",  "Ask again later.",           "My reply is no.",
    "Without a doubt.",     "Better not tell you now.",   "My sources say no.",
    "Yes – definitely.",    "Cannot predict now.",        "Outlook not so good.",
    "You may rely on it.",  "Concentrate and ask again.", "Very doubtful.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
];

var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);

上記のコードは妥当です。 うまくいっています。 ただし、再利用可能ではありません。 これでアプリケーションが動作するようになりました。次は、再利用可能なパーツを取り出します。

1 つの候補は、待機中のアニメーションを表示するコードです。 そのスニペットはメソッドになる可能性があります。

まず、ファイルにローカル関数を作成します。 現在のアニメーションを次のコードに置き換えます。

await ShowConsoleAnimation();

static async Task ShowConsoleAnimation()
{
    for (int i = 0; i < 20; i++)
    {
        Console.Write("| -");
        await Task.Delay(50);
        Console.Write("\b\b\b");
        Console.Write("/ \\");
        await Task.Delay(50);
        Console.Write("\b\b\b");
        Console.Write("- |");
        await Task.Delay(50);
        Console.Write("\b\b\b");
        Console.Write("\\ /");
        await Task.Delay(50);
        Console.Write("\b\b\b");
    }
    Console.WriteLine();
}

上記のコードでは、main メソッド内にローカル関数が作成されます。 そのコードは引き続き再利用できません。 そのため、そのコードをクラスに抽出します。 utilities.cs という名前の新しいファイルを作成し、次のコードを追加します。

namespace MyNamespace
{
    public static class Utilities
    {
        public static async Task ShowConsoleAnimation()
        {
            for (int i = 0; i < 20; i++)
            {
                Console.Write("| -");
                await Task.Delay(50);
                Console.Write("\b\b\b");
                Console.Write("/ \\");
                await Task.Delay(50);
                Console.Write("\b\b\b");
                Console.Write("- |");
                await Task.Delay(50);
                Console.Write("\b\b\b");
                Console.Write("\\ /");
                await Task.Delay(50);
                Console.Write("\b\b\b");
            }
            Console.WriteLine();
        }
    }
}

最上位レベルのステートメントを含むファイルには、最上位レベルのステートメントの後に、ファイルの末尾に名前空間と型を含めることもできます。 ただし、このチュートリアルでは、アニメーション メソッドを別のファイルに配置して、再利用しやすくします。

最後に、animations 配列で定義された一連のアニメーション要素をforeach ループを使用して反復処理することにより、アニメーションコードを整理し、重複を排除できます。
リファクタリング後の完全な ShowConsoleAnimation メソッドは、次のコードのようになります。

public static async Task ShowConsoleAnimation()
{
    string[] animations = ["| -", "/ \\", "- |", "\\ /"];
    for (int i = 0; i < 20; i++)
    {
        foreach (string s in animations)
        {
            Console.Write(s);
            await Task.Delay(50);
            Console.Write("\b\b\b");
        }
    }
    Console.WriteLine();
}

これで完全なアプリケーションが完成し、後で使用できるように再利用可能なパーツをリファクタリングしました。 メイン プログラムの完成したバージョンに示すように、最上位レベルのステートメントから新しいユーティリティ メソッドを呼び出すことができます。

using MyNamespace;

Console.WriteLine();
foreach(var s in args)
{
    Console.Write(s);
    Console.Write(' ');
}
Console.WriteLine();

await Utilities.ShowConsoleAnimation();

string[] answers =
[
    "It is certain.",       "Reply hazy, try again.",     "Don’t count on it.",
    "It is decidedly so.",  "Ask again later.",           "My reply is no.",
    "Without a doubt.",     "Better not tell you now.",   "My sources say no.",
    "Yes – definitely.",    "Cannot predict now.",        "Outlook not so good.",
    "You may rely on it.",  "Concentrate and ask again.", "Very doubtful.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
];

var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);

前の例では、Utilities.ShowConsoleAnimationへの呼び出しを追加し、別の using ディレクティブを追加します。

概要

最上位レベルのステートメントを使用すると、新しいアルゴリズムを探索するために使用する単純なプログラムを簡単に作成できます。 さまざまなコード スニペットを試すことで、アルゴリズムを試すことができます。 動作を学習したら、コードをリファクタリングして保守性を高めることができます。

最上位レベルのステートメントを使用すると、コンソール アプリに基づくプログラムが簡略化されます。 これらのアプリには、Azure 関数、GitHub アクション、およびその他の小さなユーティリティが含まれます。 詳細については、最上位レベルのステートメント (C# プログラミング ガイド)を参照してください。