C#で外部プロセス呼び出しをかんたんに扱えるようになるライブラリ ProcessX を使う機会があったんですが、その使い勝手のよさとc#/.NETの進化に驚いたので感動ポイントを共有します!
機能概要とGitHubリポジトリはこちら↓
対象読者
- C#/.NETの知識が数年前で止まっている&最近の動向が気になる方
- ProcessXの実装に興味がある方
※ライブラリの使い方については本記事では扱いません
サンプル実装
この4行のソースだけで、「dotnet --version」のコマンドを実行し、結果をコンソール出力できます。素敵。
using Zx;
using static Zx.Env;
var currentVersion = await "dotnet --version";
log($"現在の.NET SDKバージョン: {currentVersion} ");
すこしC#をかじっていればなんとなく読めると思いますが、なぜこれでコンパイルエラーなく動作してるのかキチンと説明しろといわれると困ってしまう人もいるかとおもいます。ひとつずつみていきます。
usingの数が少ない件
注目点
いつもの「Program.cs」だと
using System;
using System.Collections.Generic;
...
のようにたくさんusingが並んでいたはずですが、今回のソースにはそれが見当たりません。
意識して使わないといけない名前空間だけ記述してます。
新機能|global using
これはC# ver10.0で追加された global using という機能によるものです。
ポイントとしては下記2点。
- global using という C# の文法を追加
(globalという修飾子をつけることでプロジェクト全体に影響を及ぼすusingが定義できるように) - .NET SDK 側で、コンパイル開始時に所定の global using コードを生成
コンソールアプリの場合、以下のようなソースコードが自動生成・コンパイルされるため、ソース本体に記述するusingの数が減らせる、という仕掛けのよう。
// <autogenerated />
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks
参考文献
いつもの「おまじない」がない件
注目点
いつものProgram.csだとあるはずの「おまじない」
class Program
{
static void Main(string[] args)
{
// hogehoge
}
}
「class Program」も「static void Main...」も見当たりません 👀
新機能|top-level statements
これはC# 9.0で追加されたトップレベルステートメントという機能によるもの。
最上位に記述したメソッドがコンパイル時におまじないに包まれる形で展開されるようです
await "コマンド"
今回、一番謎だったのが
var currentVersion = await "dotnet --version";
なんだこれ。 awaitのあとに文字列??
neueccさんのブログでもコメントされてました
stringをawaitしていることに一瞬違和感はめちゃくちゃあるでしょうが、DSLだと思って慣れれば全然自然です(そうか?)。
めちゃくちゃ違和感あります。
async/awaitの仕様を再確認
普通ならコンパイルエラーになるはずなのになぜ動くのか。
ヒントはここに
await演算子に渡すことができるインスタンスは、以下の条件を満たす必要があります。
Awaiterを返すGetAwaiterメソッドを持つ
なるほど。IFもいらなくてダックタイピング的な仕様になっていると。
ライブラリのソースを探してみるとたしかにありました
namespace Zx
{
public static class StringProcessExtensions
{
public static TaskAwaiter<string> GetAwaiter(this string command)
{
return ProcessCommand(command).GetAwaiter();
}
....
}
}
string型の拡張メソッドとしてGetAwaiterが定義されてます。
stringのインスタンスをawaitにわたすと非同期処理が可能になるってことですね。
ナルホド。
まとめ
そんなこんなで、いろいろとc#/.NETが進化した結果このような便利なライブラリが誕生してました、というお話でした。C# 10.0で追加されたInterpolatedStringHandlerも活用されてるようで少し気になるところですが長くなってきたのでとりあえずこのへんで。
最後までお付き合いありがとうございました〜