"スクリプティング"言語を定義する特徴のひとつは、ボイラープレートを必要としないことだ。ファイルの最初の行から、関数の中にあるような宣言やステートメントを記述することができる。VBやC#、Javaといった言語で、何らかの"main"メソッドがクラス内に必要なのとは対照的だ。
MicrosoftのMads Torgersen氏は、C# 9でこの機能を採用することを提案している。"Proposal 3117, Top-level statement and functions"ではステートメントや関数を、クラスで包含せずにファイル内に記述できるようにする。ひとつ前の提案で氏が述べていた理由は、次のようなものだ。
現在のC#コンパイラは、さまざまなスクリプティングやインタラクティブな目的で使用されるダイアレクト(方言)を理解します。このダイアレクトでは、ステートメントをトップレベルに(メンバのボディで囲むことなく)記述することや、非仮想メンバ(メソッドなど)をトップレベルに記述することが可能になっています。
このスクリプティングダイアレクトの利用は比較的マイナーなため、いくつかの部分では"正式な"C#言語に追随できていません。一方で、Try.NETなどのテクノロジを通じてその利用が取り上げられていることから、互換性のない2つのC#ダイアレクトが戸惑いの原因になるのではないか、と危惧されます。
現時点では、C#のスクリプティングバージョンは広く使われてはいないが、将来的には変わるのではないか、とTorgersen氏は予測している。
Try.NETの存在だけではなく、データ科学やマシンラーニングのシナリオも増えています。これらは、よりダイレクトな — インタラクティブなモードでライブデータを扱うことの恩恵を受ける傾向があります。
インタラクティブ/スクリプティングC#を区別する必要がどこにあるのでしょう?"実験"と"ソフトウェア開発"の間でコードをラウンドトリップできれば、非常に便利なものになると思うのです。
シナリオ1
この提案のシナリオ1が適用されれば、ネームスペース宣言より前に実行ステートメントを置くことが可能になる。これらのステートメントは、Programという名称のクラス内のMain関数の中にコンパイルされる。この関数は非同期操作をサポートする。
複数のファイルにネームスペース外の実行ステートメントがあると、コンパイルエラーが発生する。具体的には、Main関数を持つProgramという名前のクラスが複数存在する、ということになる。
シナリオ2
シナリオ2では、トップレベルの関数も許容される。この関数はネームスペース内でも、グローバルにも宣言が可能だ。デフォルトではinternalだが、public宣言することもできる。呼び出し側からは、関数がネームスペースに直接属するように見える。(この動作方法は、Visual Basicのモジュールにすでに存在する。)
実装としては、そのメンバを静的メンバとしてラップするパーシャルクラスが生成される。クラス名には、読み上げられないような名称が、異なるアセンブル間の同じネームスペース内で重複して使用されないように選択される。トップレベルのメンバにpublicのものがあれば、クラスはpublicになり、公開されるメンバが参照する側のアセンブリに直接分かるような方法でマークされる。
シナリオ3
C#にはスクリプティングダイアレクトがすでに存在するが、C#自体とは違うものだ。このシナリオの目標はスクリプティングダイアレクトを廃止することではなく、2つの言語を互いに近付けることにある。Torgersen氏が説明する。
C#にトップレベルのステートメントや関数を追加するならば、スクリプトで動作するものと干渉するようにはしたくありません。必要に応じて必要な方法でコンパイルしたいとは思いますが、機能のセマンティクスは統一したいのです。特別なディレクティブや"マジックコマンド"は依然として必要なので、これでスクリプティングダイアレクトが完全になくなる訳ではありませんが、少なくとも同じ構文が実質的に違うものを意味するようなことは避ける必要があります。
現時点で氏は、C#がシナリオ1のみに注目することを提案している。
すべてのシナリオを提供する統合機能を垣間見たり、想像したりすることは可能ですが、実現には非常に多くの設計作業と、いくつかの点での妥協が必要になります。そんな提案はしません。それよりもシナリオ1を完全に実現するべきだと思うのです。基本的には、上記のシナリオでスケッチした機能を具体化して、少し一般化したものです。
さらに氏は、シナリオ1のすべての機能の導入は、将来的なシナリオ2や3の実装を阻害しない方法で行う必要がある、と説明している。
設計詳細
トップレベルステートメントの最初のルールは、プロジェクト内で1つのファイルのみに記述する、というものだ。"Main"関数がひとつだけであるように、裸のステートメントが複数のファイルにある場合はコンパイルエラーになる。
生成されるコードの形式は、ステートメントの内容によって決まる。await
を使うか使わないか、式を伴うreturn
ステートメント(return 5
のように)があるかないか、によって4つの可能性がある。
static void Main(string[] args)
static int Main(string[] args)
static Task Main(string[] args)
static Task<int> Main(string[] args)
通常のメソッドで使用するものと同じ構文を使えば、ローカル関数を使用することも可能だ。