3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「動くはずなのに…」System.Text.Json でハマった実行時例外の話

Posted at

「コンパイルは通るのに、なぜか実行時だけ例外が出る」

System.Text.Json 周りで、そんな現象に遭遇しました。

  • 型は見える
  • メソッドも存在する
  • でも実行すると落ちる

原因を追っていくと、
C# の書き方ではなく、.NET(CLR)の挙動に行き着いた、という話です。


起きていたこと(要約)

  • NuGet 経由で System.Text.Json を参照している DLL が複数存在
  • それぞれが 異なるバージョンの System.Text.Json を前提にビルドされていた
  • 使用している API はどのバージョンにも存在していたため コンパイルは問題なし
  • 実行時にロードされた実体が合わず、例外が発生

なぜコンパイルは通ったのか

コンパイル時に参照しているのは 参照アセンブリ です。

参照アセンブリに含まれるのは、

  • 型名
  • メソッド名
  • 引数・戻り値

といった シグネチャ情報のみ です。

public static string Serialize<T>(T value);

この定義が複数バージョンに存在していれば、
コンパイラはそれらの違いを認識できません。

「そのメソッドが存在する」ことが分かれば OK

という世界です。


実行時に何が起きるか

実行時に必要になるのは 実行用アセンブリ(実装 DLL) です。

ここで初めて、

  • IL の中身
  • 内部実装
  • バージョン依存の処理
    が要求されます。

その結果、

  • MissingMethodException
  • TypeLoadException
  • FileLoadException
    といった例外が発生しました。

ファイルサイズで気づけることもある

後から振り返ると、
ロードされていた DLL の ファイルサイズが異常に小さい ことに気づきました。

  • 実行用 DLL:数百 KB 〜 数 MB
  • 参照用 DLL:数十 KB 程度

「コードが無いように見える」

という感覚は、実は正しかったようです。


学んだこと

  • 参照が通る ≠ 実行できる
  • 参照アセンブリは「契約」しか保証しない
  • 実体をロードするのは CLR
  • 問題はコードではなく、依存関係と解決経路 にあることが多い

再発防止として意識していること

  • BCL(System.*)を NuGet でむやみに追加しない
  • 上位 DLL の TargetFramework を必ず確認する
  • 実行フォルダの DLL の サイズと配置場所を見る
  • .deps.json / runtimeconfig.json を疑う

おわりに

今回の件で、

「C# を書いている」
から
「.NET がどう動くかを意識する」
という視点に一段階進んだ気がしています。

同じように、

  • 参照は通るのに実行時だけ落ちる
  • 環境差分で原因不明の例外が出る
    といった経験をした人の、整理材料になれば幸いです。

参照が通ることと、実行できることは別物

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?