OUCC

BLOG

記事一覧 タグ一覧

dotnet-scriptを使ってC#でも書き捨てプログラムを

投稿日:

執筆者: しおかい

これは OUCC Advent Calendar 2022の12日目の記事です。 本題まで長いので読み飛ばしていただいて結構です。

動機

大学の講義というものは、工学部・基礎工学部に限らずプログラムを書かされることは間々あります。 そういったときに、特に言語を指定されなければ自分の慣れている言語で書きたいものです。

ところで、筆者にとってその「慣れている言語」というのがC#なわけですが、これがどうにも講義で使うのには相性が良くないのです(個人の感想です)。

講義でプログラムを求められる状況では、うちの場合大抵その一回限りの使い捨ての、コンソールアプリとすら言わないような実行して出力を見るだけといったようなものを書くことになります。 C++やPythonであれば、(予め環境構築が終わっていれば)適当に1つファイルを作って書き捨て、実行すればそれでいいので何も思うところはありません。

ところがC#(.NET)では、一番簡単なコンソールアプリでも、専用にフォルダを用意し、dotnet new consoleしてプロジェクトを作らねばなりません。 しかも、プログラムが記述されたファイルと実行ファイル以外にいろいろファイルやフォルダが生成される始末で、「書き捨て」をするには余りに仰々しすぎるのです。

試行錯誤

もっと、それこそC++やPythonのように、プログラムが記述されたファイルと、せいぜいコンパイル後の実行ファイル程度でどうかしたい。 そう思った私は、「そもそもdotnet経由で実行しようとするからプロジェクトが要るのでは」と思い、csc.exe(CSharp Compiler)を直接叩いてコンパイルしようと思い立ちました。 実際これでコンパイル、実行自体はできるのですが、いくつか問題がありました。

インテリセンスが効かない

まず、始めにやってみて気が付いたのは、単に.csのファイルを開くだけでは、VSCodeでインテリセンス等が全くと言っていいほど効かないのです! C#と言えば静的解析(ほんとに?)。 それが効かないのは余りに面倒でした。

ではcscを叩く方法でC#にインテリセンスを効かすにはどうするか。 いくつか方法はあるのですが、最も単純なのは、*.csprojをワークスペースに配置することです。 これはdotnet new consoleすれば簡単に生成できます。 結局dotnet new consoleが必要なものの、1回で済むならだいぶ状況は良くなったと、初めはそう思いました。

フォルダ内にエントリポイントがあるファイルを複数置けない

この方法の問題点は何か。それは、VSCodeで開いたフォルダ全体が1つのプロジェクトとして認識されるため、エントリポイントを1つしか持てないということです。 言い換えれば、Mainのあるファイルがフォルダ内に複数あると、VSCode上でエラー表示が出るのです。

もちろん実際にはcscでコンパイルするのでエラーは無視できるのですが、うっとうしいですし、本当にエラーになっているものを見逃すかもしれません。 適当にファイルの中身の切り貼りを自動化すれば、一応大した手間なくエラーも無くコンパイルもできる状態にはできるものの、各.csファイルは単体でコンパイル/実行不可能なうえ、自動化用の諸々(当時私は専用のC#プログラムとtask.jsonで行っていました)がフォルダ内に散乱して、結局当初の目的をあまり達成できていませんでした。

光明

さてそうこうしいたある時、「そういえばC#をインタープリタ的に実行できるのがあった気がする」と思い出しました。 それについて調べたところ、dotnet-scriptというものを見つけ、これがまさに自分の求めていたものでした。 [注釈1]:ところで、先ほど「いくつか方法はある」といったところにC# scripts (CSX)と書いてあり、これがそのdotnet-scriptのファイルなのですが、ここに書いてあったことにこれを書いている今気が付きました。 (OUCC BLOGってMarkdonwのfootnote記法使えないのか……)

というわけで、今回このC# scriptsについての紹介になります。 (ここまで前置き)

本題

インストール

C# scripts(dotnet-script)とはどういうものなのかの話の前に、簡単にインストール方法を説明しておきます。 といっても、.NET6か.NET7のSDKをインストールすれば、あとはコマンドラインからdotnet tool install -g dotnet-scriptをするだけです。

できること

dotnet-scriptでは以下のことができるようです

  • アプリケーションへの埋め込み
  • Interactive実行(これが先ほど言った「インタープリタ」的実行)
  • スクリプト実行(主目的)

このうちアプリケーションへの組み込みは今回説明しません。

Interactive実行

コマンドラインでdotnet scriptまたはdotnet-scriptと入力することで、インタプリンタ環境が起動します。 文法は通常のC#とほぼ同じです。 リテラルや変数を;無しで打つと値を表示してくれます。 またおそらくImplicitUsing(後述)によるものですが、初めからある程度using済みなので、少し変わった電卓程度の用途には十分使えます。

なお起動後初めの出力が異様に遅いので、適当に数字を打って出力させておくといいでしょう。

一応後述のスクリプトファイルを読み込んだり、NuGet参照したもできますが、あまり複雑なことをするには(当然ながら)向いてはいません。 せいぜい打ち間違いに強い電卓でしょう(個人のry)。

なお終了は#exitで行えます。

スクリプト実行

さて本題、求めていたスクリプト実行です。 VSCodeの場合、ワークスペースでdotnet script initとすれば、直下にomnisharp.jsonmain.csxが、.vscode下にlaunch.jsonが生成されます。

これができればあとは好きにC#を書いていけます。 新規にスクリプトファイルを生成するのはdotnet-script new filenameでできます。 (普通にファイルを作っても問題ありませんが、たまにスクリプトファイルとして認識してくれずインテリセンス等が効かなくなることがあります。その場合、VSCodeを開きなおせば問題ありません。)

注意点として、Mainを書くと、実行時に warning CS7022: プログラムのエントリ ポイントは、グローバル コードです。エントリ ポイント 'test.Main(string[])' を無視します。と言われて読み飛ばされてしまうので、トップレベルステートメントの記法を使用しましょう。 また、同じワークスペースで拡張子が.csのファイルを開いてしまうとインテリセンス等が効かなくなる場合があるので、拡張子を間違えないようにしましょう。 スクリプトファイルの拡張子は.csxです。 万一インテリセンスが効かなくなっても、.csなファイルを閉じてからVSCodeを開きなおせば問題ありません。

またImplicitUsingと思われる暗黙的にusingされている名前空間もあります。 一覧を見つけられなかったので、VSCodeのオートコンプリートを使って、aからzまで一文字打っては補完候補を見ていくという頭の悪い手法で調べたところ、dotnet-scriptで暗黙的にusingされているのはおそらく以下の通りです。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Text;
using System.Diagnostics;
using System.Dynamic;
using static System.Console;

コンソールアプリケーションとも少し違うようです。 System.Consoleusing staticされているおかげで、標準出力がWriteLine()で済むので楽です。

NuGet参照も使え、ファイル先頭に#r "nuget: PackageName, 1.0.0"のように書くことで参照できます。 ただし、これを追記した後はVSCodeを開きなおさないと、名前空間等をVSCodeが認識してくれないのが難点です。

また、実行はdotnet-script filepathで行えます。 VSCodeならdotnet script initしていればlounch.jsonが生成されているはずなのでF5でもいいでしょう。

一応コンパイル(発行)もdotnet-script publish filepathで行えます。 ただし何故かワークスペース直下にあるファイルをpublishできないので、コンパイルしたい場合は一段下の階層に入れましょう。

詳細な記法はGitHubを見るといいと思います(ほぼトップレベルステートメントと変わりませんが)。

トップレベルステートメントやImplicitUsingが分からない場合、アドベントカレンダー7日目の私の記事を見ていただければと思います(露骨な宣伝)。

終わりに

というわけで、dotnet-scriptによるスクリプト実行によって、C#で快適に書き捨てプログラムを書けるようになりました。 これで、「『ファイルを作ってハイ実行』って訳にはいかないから気軽にC#を使えない」と言ってC++やPythonを使っていた皆さんも、思う存分C#を書けますね!