bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] dynamic と式木で動的な呼び出しを理解する

[C#] dynamic と式木で動的な呼び出しを理解する

Jul 10, 2026 C# , .NET bucket-sort

前回は、プラグインで拡張できるアプリケーション構造を作りました。

今回は、dynamic を使った動的な呼び出しを見ていきます。
dynamic は、コンパイル時ではなく実行時にメンバーを解決するための仕組みです。

リフレクションより簡潔に書ける場面がありますが、型安全性を一部手放すため、使いどころを選ぶ必要があります。

dynamic とは

通常の C# では、メソッドやプロパティの存在はコンパイル時にチェックされます。

string text = "hello";
Console.WriteLine(text.Length);

Length が存在しなければ、コンパイルエラーになります。

一方、dynamic を使うと、メンバーの存在チェックが実行時に遅れます。

dynamic value = "hello";

Console.WriteLine(value.Length);

このコードは実行時に Length を探します。
もし存在しないメンバーを呼び出すと、コンパイルエラーではなく実行時例外になります。

dynamic value = "hello";

// 実行時に失敗する
Console.WriteLine(value.UnknownMember);

動的に宣言した値のメンバーを呼ぶ

dynamic の便利なところは、メンバー呼び出しの見た目が普通の C# とほぼ同じになる点です。

public class Greeter
{
    public string SayHello(string name)
    {
        return $"Hello, {name}";
    }
}
dynamic greeter = new Greeter();

string message = greeter.SayHello("Alice");

Console.WriteLine(message);

リフレクションで同じことを書くと、次のようになります。

object greeter = new Greeter();
Type type = greeter.GetType();
MethodInfo? method = type.GetMethod("SayHello");

object? result = method?.Invoke(greeter, new object?[] { "Alice" });

Console.WriteLine(result);

dynamic の方が短く、読みやすい場面があります。

dynamic の適用範囲

dynamic は、変数、引数、戻り値、コレクション要素などに使えます。

static dynamic CreateValue(bool text)
{
    return text ? "hello" : 123;
}

dynamic value = CreateValue(true);
Console.WriteLine(value.Length);

メソッド引数にも使えます。

static void PrintName(dynamic value)
{
    Console.WriteLine(value.Name);
}

ただし、このメソッドは Name を持たない値が渡されると実行時に失敗します。

PrintName(new { Name = "Alice" });
PrintName(123); // 実行時に失敗

dynamic は便利ですが、呼び出し側と呼び出される側の契約がコード上で見えにくくなります。

dynamic の制限

dynamic は何でも解決してくれる魔法ではありません。

主な制限や注意点は次の通りです。

  • メンバーの存在チェックが実行時になる
  • リファクタリングで検出しづらい
  • IntelliSense が効きにくい
  • 実行時例外が増えやすい
  • API の契約が読み取りにくくなる

そのため、アプリケーション全体に広げるより、境界部分だけで使うのが安全です。

たとえば、外部サービスから得た柔軟な JSON、COM 連携、スクリプト連携、リフレクション呼び出しの薄いラッパーなどです。

実用的な使いどころ

dynamic が役立つ代表例をいくつか見てみます。

形が変わるデータを一時的に扱う

dynamic item = new
{
    Id = 1,
    Name = "Sample"
};

Console.WriteLine(item.Name);

ただし、長く使うデータ構造なら、クラスや record を定義した方が安全です。

リフレクション呼び出しを簡潔にする

object instance = Activator.CreateInstance(typeof(Greeter))!;
dynamic dynamicGreeter = instance;

Console.WriteLine(dynamicGreeter.SayHello("Alice"));

メソッド名を文字列で指定するより自然に書けます。
ただし、メソッドが存在しない場合は実行時に失敗します。

相互運用の境界で使う

COM やスクリプト的な API など、コンパイル時に型情報が扱いづらい相手との境界で役立つことがあります。

アプリ内部のドメインロジックでは、通常の静的型付けを使う方が保守しやすいです。

動的な呼び出しを支える仕組み

dynamic の裏側では、動的なメンバー解決を支えるランタイムの仕組みが働きます。

C# コンパイラーは、dynamic な呼び出しを通常の静的呼び出しとして確定しません。
実行時に、対象オブジェクトの型や呼び出し内容をもとに、どのメンバーを呼ぶかを解決します。

この仕組みにより、C# から動的言語的な呼び出しができます。

ただし、実行時解決にはコストがあります。
大量に繰り返される処理の内部で無造作に使うと、性能や可読性に影響する可能性があります。

式木とは

式木は、コードを「実行するもの」ではなく「データ構造」として表現する仕組みです。

using System.Linq.Expressions;

Expression<Func<int, int, int>> expression = (x, y) => x + y;

Console.WriteLine(expression);

この例では、x + y という式が、実行結果ではなく構造として保持されます。

式木は、LINQ プロバイダー、ORM、動的クエリ生成などで使われます。
たとえば Entity Framework Core は、式木を解析して SQL に変換します。

式木を実行する

式木は、コンパイルしてデリゲートとして実行できます。

using System.Linq.Expressions;

Expression<Func<int, int, int>> expression = (x, y) => x + y;

Func<int, int, int> add = expression.Compile();

Console.WriteLine(add(2, 3));

式木は「実行できるコード」でもあり、「解析できるデータ」でもあります。

プラグインやルールエンジンを作る場合、条件式をデータとして組み立てる用途でも使えます。

dynamic で遅延呼び出しを簡潔にする

リフレクションでメソッドを呼ぶと、どうしてもコードが長くなります。

object plugin = Activator.CreateInstance(pluginType)!;
MethodInfo? method = pluginType.GetMethod("Execute");
method?.Invoke(plugin, new object?[] { "input.csv" });

dynamic を使うと、見た目は普通のメソッド呼び出しに近くなります。

dynamic plugin = Activator.CreateInstance(pluginType)!;
plugin.Execute("input.csv");

引数も自然に渡せます。

dynamic plugin = Activator.CreateInstance(pluginType)!;

string result = plugin.Convert("input.csv", "output.json");

Console.WriteLine(result);

ただし、プラグイン設計では、可能なら共通インターフェイスを使う方が安全です。
dynamic は、どうしても静的な契約を作りにくい境界で使う補助的な選択肢と考えるのがよいです。

プラグイン設計での使い分け

拡張可能なアプリケーションでは、次の順で検討すると整理しやすいです。

まず共通インターフェイスを使う

もっとも安全です。
コンパイル時に契約を確認でき、呼び出し側も読みやすくなります。

public interface IPlugin
{
    Task ExecuteAsync(CancellationToken token);
}

属性で発見しやすくする

インターフェイスだけでは表示名やカテゴリが不足する場合、属性を追加します。

[Plugin("CSV 取り込み", Category = "Import")]
public sealed class CsvPlugin : IPlugin
{
    public Task ExecuteAsync(CancellationToken token)
    {
        return Task.CompletedTask;
    }
}

dynamic は境界を小さくして使う

どうしても実行時まで形が分からない相手には dynamic を使えます。
ただし、呼び出し箇所を少なくし、例外処理を厚めにします。

try
{
    dynamic plugin = Activator.CreateInstance(pluginType)!;
    plugin.Execute();
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
{
    Console.WriteLine($"呼び出しに失敗しました: {ex.Message}");
}

まとめ

dynamic は、実行時にメンバーを解決するための仕組みです。
リフレクションより短く書ける場面がありますが、型安全性や保守性を失いやすいため、使いどころを選ぶ必要があります。

式木は、コードをデータ構造として扱う仕組みです。
LINQ プロバイダーや動的なクエリ生成など、実行前に式を解析したい場面で活躍します。

Chapter 17 のテーマ全体を通して見ると、型メタデータ、リフレクション、属性、動的呼び出しは、どれも「実行時にアプリケーションを理解し、拡張する」ための技術です。
これらを設計の道具として使えるようになると、プラグイン、診断ツール、ルールエンジン、フレームワーク的な仕組みを作る力がぐっと伸びます。

C# .NET Dynamic DLR 式木 リフレクション 遅延バインディング
← [C#] プラグインで拡張できるアプリケーションを設計する [C#] CIL を読む理由と基本文法を理解する →

Related Posts

  • [C#] プラグインで拡張できるアプリケーションを設計する Jul 9, 2026
  • [C#] 属性でコードに意味を与える Jul 8, 2026
  • [C#] 外部アセンブリを読み込み実行時に型を呼び出す Jul 7, 2026
  • [C#] リフレクションで型の中身を調べる Jul 6, 2026

Table of Contents

  • dynamic とは
  • 動的に宣言した値のメンバーを呼ぶ
  • dynamic の適用範囲
  • dynamic の制限
  • 実用的な使いどころ
    • 形が変わるデータを一時的に扱う
    • リフレクション呼び出しを簡潔にする
    • 相互運用の境界で使う
  • 動的な呼び出しを支える仕組み
  • 式木とは
  • 式木を実行する
  • dynamic で遅延呼び出しを簡潔にする
  • プラグイン設計での使い分け
    • まず共通インターフェイスを使う
    • 属性で発見しやすくする
    • dynamic は境界を小さくして使う
  • まとめ

Recent Posts

  • [C#] EF Core でデータベースの準備と初期データを扱う Aug 8, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026
  • [C#] EF Core のエンティティと表示用モデルを設計する Aug 5, 2026
  • [C#] EF Core を中心にデータアクセス層を分ける Aug 4, 2026

Categories

  • C#150
  • .NET149
  • AWS27
  • Laravel16
  • Entity Framework Core15
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • Data Access6
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • Entity Framework Core
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • LINQ
  • System.Collections.Generic
  • デリゲート
  • リフレクション
  • ADO.NET
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メタデータ
  • メモリ管理
  • CIL
  • DoS
  • Nginx
  • SQL Server
  • WordPress
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • アセンブリ
  • インターフェース
  • オブジェクト指向
Powered by Hugo & Explore Theme.