C#erのみなさんこんばんは。暖かくなってきましたね。ハハハ。フハハハハハwwヒヒィィンwwwwwww

しょうもねぇツールが欲しくって、毎日楽器屋に通って窓に顔こすりつけて眺めてたんだけど誰も買ってはくれず、致し方が無く自分でしょうもねぇツールを書いた。

GitHub - Connect-a/GomiDownloadTools

そのツールの紹介とかしたいわけじゃなくて。真面目な気持ちで書いていたら.NET開発世界に対して色々思うことがあったからそれを綴る。

ついでに知っ得情報みたいなのもまとめてみる。

◆余談

Rustで書こうかなとか思ったんだけど、DIとHTTPクライアントの挙動がよくわかんなかったのでやめた。久々にHaskell書こうかなとかもチラッと頭よぎったけど綺麗には書けないだろうと思う。まぁC#っすわ。綺麗に書く必要あるなと思ったらC#っすわ。Python?あんなカス言語で書くわけないだろ。設計が収まらねぇわあのゴミ。Rustのほうが大分マシ。

プロジェクトを厳密に分けようかとも思ったんだけど、そこまでする必要は無いな。ちょっとしたものですし。

◆C#

今思えばインデントが2になっちゃってる。なんか最近2なんだよな。規約的には4なんだけど。

C# のコーディング規則 | Microsoft Docs

▼トップレベルステートメント

最上位レベルのステートメント - C# チュートリアル | Microsoft Docs

これのせいでインデントが2になっちまったんだよなぁオイコラァ。感覚的にTypeScript書いてる感じになってしまう。

ただ、なんやかんや悩んだ結果としてProgram.csはほぼ空になった。

GomiDownloadTools/Program.cs at main · Connect-a/GomiDownloadTools · GitHub

GitHubのコードってGistみたいな埋め込みできないんだな。EmGithubってのがあったんでそれを使ってみたんだけど、styleが崩れた。食い合ったのかな。等幅フォントにすらならなかったんだが。

あとImplicitUsingsとかでglobal usingで。大きいプロジェクトでは使う気にならんけど、こういうしょぼくれたツールを書くくらいであったら便利ね。

▼ファイルスコープ名前空間

File scoped namespaces - C# 10.0 draft specifications | Microsoft Docs

namespaceのブレースを失くして、ファイル全体に対するnamespaceを設定できる。ネスト警察からしたらありがたい変更。1ファイルにふたつの名前空間書いてるやつなんて見たことないし。

▼System.CommandLine

コンソールアプリのパラメータについてパーサーどうしようかねと思ったんだけど、System.CommandLineというのがあるらしかった。

GitHub - dotnet/command-line-api: Command line parsing, invocation, and rendering of terminal output.

それまでもコマンドラインパーサー周りではMicrosoft謹製プロジェクトが立ち上がっては消えを繰り返してたらしい。

とりあえず導入したんだが、まーインターフェースがおかしい。なんらか設計がおかしい。redundant。いま2.0のbeta3が走ってるんだけど、beta2と合わせてツッコミを食らいまくっている。

DragonFruitってサブプロジェクトもあるんだけど柔軟性とか皆無で使う気にならん。

あとドキュメントの書きっぷりがちょい変。それ通りに書こうと思うとそれこそ冗長になる。

考え倒した結果以下の構成になったので興味あったら見てみてくれ。

GomiDownloadTools/Gomi/CLI at main · Connect-a/GomiDownloadTools · GitHub

System.CommandLineに対して納得いってないポイントについて語る。文句がニッチすぎるので読まなくていい。

・DI

あたしゃ依存性注入の依存症ですから、DIできないとつらい。組み込みのDIが欲しい。「ConsoleAppだから起動パフォーマンスが不利」みたいな書かれ方されてて、死ねと思った。コンマ何秒だよ。実行効率よりアーキなんだよクソどもが…!

しょうがねぇので自分でnew ServiceCollection()した。書いてる場所がよろしくないんだけど、まぁ今はいいや。フレームワーク書くみたいな話になりかねん。

・BinderBase

最終的にデータオブジェクト以外全部staticクラスになると思ってたんだけど、コマンドライン引数をクラスにマッピングするためのBinderBaseってアレを継承せねばならず、そこだけstaticにできなかった。しかしマッパーとかインスタンスにする必要ないだろ。

・IValueDescriptor

IValueDescriptorってインターフェースの使い方がひどい。アホなの?あたまわるいわ。こんな設計力で.NET周辺の開発しとるんか。ヘルスバーグに土下座してきやがれ。

OptionArgumentはまだわかるんだけど、BinderBaseがIValueDescriptorを実装してるのは擁護できない。SetHandlerにparamsで渡したいがために何でもかんでもIValueDescriptorにしてんだよコイツ。それをGetValueForHandlerParameterっていうあたまわるいメソッドに通してどうにかしてんの。

System.Reflection使いすぎてるのもキモい。そんな人間がパフォーマンスどうこう言ってんかと。参照も追えなくなってない?

書いてる途中でおかしいと思わなかったんだろうか。コイツのプルリク通さないわ俺。倫理に悖る。

みたいなことが結構沢山あるので、将来的にこのリポジトリは潰れるでしょう。設計がおかしい。

▼is

GomiDownloadTools/Builder.cs at main · Connect-a/GomiDownloadTools · GitHub

ifで型チェックをしつつインスタンスをアレできる。ダウンキャストできる。

Pattern matching - C# 7.0 specification proposals | Microsoft Docs

System.CommandLineのインターフェースが腐ってるからこんな処理を書いてる。

if (p is Option o) cmd.Add(o);

「変数pがOptionクラスであった場合に」のif文において、C#6以前ではpをOptionにキャストせねばOptionとして扱えなかったんすね。でも「Option o」とかって書いてやればOptionのoを作れる。便利。

notとかも書けるようになったんで

if (p is not null)

的なnull判定もできるよ。

▼newのクラス名省略

Target-typed new expressions - C# 9.0 specification proposals | Microsoft Docs

GomiDownloadTools/CommandArgs.cs at main · Connect-a/GomiDownloadTools · GitHub

public static readonly Option<FileInfo?> TemplateOption = new(new string[] { "--template", "-t" }, "Download template file");

newって右側にクラス名が入りがちなんだけど、C#9からは型推論が効いてnew()みたいな書き方できる。便利ね。

▼initセッター

Init only setters - C# 9.0 specification proposals | Microsoft Docs

GomiDownloadTools/Parameter.cs at main · Connect-a/GomiDownloadTools · GitHub

get; set;でプロパティの自動実装ができるってことはご存じだろうけど、再代入できないinitってのが使える。ただ、record使える場所ではrecord使いたい。

▼using宣言

Pattern based using and using declarations - C# 8.0 specification proposals | Microsoft Docs

ブロック外れたらIDisposableで破棄される変数を作れます。今まではusingでブロックを書かねばならんかったんだが、変数宣言の前にusingを書いてやることでDisposeできるならもっと便利ね。

▼System.Text.Json

MicrosoftのJSONライブラリ。

NewtonsoftのJsonしか使いようがなかったけど、少し前からMSからJSONライブラリが提供されてんな。機能足りてなかったところも補完されつつある。

Utf8Jsonは、なんかのバグがあって親がぶっ殺された。

Deserializing string with escaped backslash fails · Issue #121 · neuecc/Utf8Json · GitHub

これだな。普通にシステム止まってAzureMonitorが激怒してて草生えたわ。

4年放置してメンテ引継ぎすらしてねぇのにこれだ。厚顔無恥。技術者の屑。「もうメンテしてませんよ」の一文を書く手間すら惜しんでるのか。

2022/6/17追記:5/17にリポジトリがアーカイブにされてた。最低限の責任感はあったらしい。偉い。技術者の鑑。抱いて。

▼Parallel.ForEachAsync

あとあと潰すかもしれんけどParallel.ForEachAsyncを使ってみている。.NET6で追加された。

Parallel.ForEachAsync メソッド (System.Threading.Tasks) | Microsoft Docs

俺の使い方あってんのかよくわかんない。

◆余談:GitHubでLISENCE生やす

リポジトリへのライセンスの追加 - GitHub Docs

◆今後

テスト書いてGitHub Action書いてeditorconfig書いてドキュメンテーションコメント書いて機能追加してバージョン1.0.0にする。みたいな。

あと日本語ドキュメントも一応書くかな。みたいな。

◆結

そんな感じ。みたいな。