VisualStudio2022をインストールしてコンソールアプリのプロジェクトを作成してみたら、
すごくすっきりしていたので調べてみた。
コンソールアプリのプロジェクトテンプレートが変わった
おなじみの「Hello, World!」を表示するプログラムが1行で書けるようになっている。
1行目のコメントはテンプレートが変わったよというMicrosoft公式のお知らせページのURL
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
これはC#9.0で追加された機能で「最上位レベルのステートメント(Top-level statements)」といい、usingとかnamespaceとかclassとかMainメソッドとかを書かずにファイルの先頭からコードを書き始めることができるようになった。
VisualStudio2022ではコンソールアプリプロジェクトのテンプレートはC#を初めてさわる人が簡単にコードを書けるようにだったり、ちょっとしたコードを手軽に書けるようにMainメソッドを省略したテンプレートがデフォルトになったらしい。
コンソールアプリのプロジェクトテンプレートの変更は賛成か反対か投票をしているみたいです。
ちなみに、以前と同じように記述してもちゃんと動く。
using System;
namespace TestProject //名前空間名は実際のプロジェクト名に置き換える
{
class Program
{
static void Main()
{
Console.WriteLine("Hello, World!");
}
}
}
ので、前の形式でコードを書きたい場合は、手動でMainメソッド部分を書いたり、コピペしてきてからコーディングを始めてください、とのこと。
古い形式を使用したい場合は、この記事の 2 番目の例のコードをコピーし、前と同様にチュートリアルを続行できます。
https://docs.microsoft.com/ja-jp/dotnet/core/tutorials/top-level-templates
いつの間にやらプロジェクトの選択時に最上位レベルのステートメントを使用するかが選択できるようになっていた。(2024/02/29追記)
省略したMainメソッドの引数や戻り値は?
Mainメソッドを省略した場合、コンパイラが自動でProgramクラスとMainメソッドを作成してプログラムを実行しているらしい。ILSpyを使うと「Console.WriteLine(“Hello, World!”);」1行のコードがこんな感じに展開されていた。
// Program
using System;
using System.Runtime.CompilerServices;
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
本題、コマンド引数には変数argsでアクセスでき、returnを使って終了コードを返すことができる。
awaitを使った場合、ちゃんと非同期Mainメソッド(asyncが付く)としてプログラムを実行してくれる。
Console.WriteLine("引数の数=" + args.Length.ToString());
var response = await new HttpClient().GetAsync("https://yaspage.com");
Console.WriteLine(response.StatusCode);
return 10;
これもILSpyで見てみるとこんな感じ、
// Program
using System;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
[CompilerGenerated]
internal class Program
{
private static async Task<int> <Main>$(string[] args)
{
Console.WriteLine("引数の数=" + args.Length);
Console.WriteLine((await new HttpClient().GetAsync("https://yaspage.com")).StatusCode);
return 10;
}
}
using部分はどうなった?
usingディレクティブはすでに以下のよく使う名前空間が宣言されている状態になっている。
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
「暗黙的なusingディレクティブ」というらしい。この宣言はプロジェクト全体に対して有効になっている。ほかにusingが必要な場合はファイルの先頭部分にまとめて記述する(それ以外の場所はエラーになる)。
using System.Diagnostics;
using System.Data;
Debug.WriteLine("Debug Print.");
DataTable tbl = new DataTable();
暗黙的なusingディレクティブを無効するにはプロジェクトのプロパティを変更する必要がある。
プロジェクトファイルを直接いじっても無効にできる。ImplicitUsingsプロパティをdisableかfalseに設定。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
そのほか制限とか注意点とか
- 最上位レベルでコードを書けるファイルはプロジェクト全体で 1 つだけ。他のファイルには今まで通り記述していく。
- Mainメソッドを省略した場合はMainという名前のメソッドは定義しないほうが幸せ。
定義はできるがエントリーポイントではなく通常のメソッド扱いになる。 - 最上位レベルでコードを書いたファイルに型や名前空間を定義する場合は、最上位レベルのコードより後ろに書く。
using TestProject;
TestClass.TestMethod();
namespace TestProject
{
public class TestClass {
public static void TestMethod() => Console.WriteLine("Test Method.");
}
}
- 最上位レベルでメソッドを定義した場合、自動で作成されるMainメソッド内のローカル関数という扱いになるみたい。
TestMethod();
void TestMethod() => Console.WriteLine("TestMethodが呼ばれました");
というコードをILSpyで確認すると、
// Program
using System;
using System.Runtime.CompilerServices;
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
TestMethod();
static void TestMethod()
{
Console.WriteLine("TestMethodが呼ばれました");
}
}
}
以上。
参考記事
https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-9#top-level-statements
https://docs.microsoft.com/ja-jp/dotnet/csharp/fundamentals/program-structure/top-level-statements
https://docs.microsoft.com/ja-jp/dotnet/csharp/whats-new/tutorials/top-level-statements
https://docs.microsoft.com/ja-jp/dotnet/core/tutorials/top-level-templates
C# 記事まとめページに戻る(他のサンプルコードもこちら)
コメント