bucket-sort logo bucket-sort

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

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] 匿名型(Anonymous Types)の使い方

[C#] 匿名型(Anonymous Types)の使い方

Jun 8, 2026 C# , .NET bucket-sort

C# では、クラスや構造体をあらかじめ定義しなくても、匿名型(anonymous type) を使うことで、その場限りのオブジェクトを手軽に作れます。

var person = new { Name = "Alice", Age = 30 };
Console.WriteLine(person.Name); // Alice

LINQ の select 句で結果を整形するときによく使われますが、単独で使っても便利です。この記事では次の 5 つを順に説明します。

  1. 匿名型の定義
  2. 匿名型の内部表現
  3. ToString と GetHashCode の実装
  4. 匿名型の等価性
  5. 匿名型を含む匿名型(ネスト)

匿名型の定義

匿名型は new { プロパティ名 = 値, ... } の形で定義します。型名を書かないため、変数には var を使います。

var car = new
{
    Make  = "Toyota",
    Model = "Prius",
    Year  = 2024
};

Console.WriteLine(car.Make);  // Toyota
Console.WriteLine(car.Model); // Prius
Console.WriteLine(car.Year);  // 2024

プロパティは定義した時点で型が推論されます。car.Make は string、car.Year は int です。

プロジェクション初期化子

既存の変数名からプロパティを作るとき、プロパティ名を省略できます。これをプロジェクション初期化子(projection initializer) といいます。

string name = "Bob";
int age = 25;

// プロパティ名は変数名と同じ Name, Age になる
var person = new { name, age };

Console.WriteLine(person.name); // Bob
Console.WriteLine(person.age);  // 25

読み取り専用

匿名型のプロパティは読み取り専用です。定義後に値を変更しようとするとコンパイルエラーになります。

var point = new { X = 10, Y = 20 };
point.X = 99; // コンパイルエラー: プロパティまたはインデクサーに値を割り当てられません

匿名型の内部表現

匿名型は、コンパイル時にコンパイラが自動でクラスを生成します。生成されたクラスは次のような特徴を持ちます。

  • sealed class として生成される
  • プロパティは get のみ(読み取り専用)
  • object を継承する

IL(中間言語)レベルでは、おおむね次のようなクラスが自動生成されます。

// コンパイラが自動生成するクラスのイメージ
// (実際にはこの名前ではなく '<>f__AnonymousType0`2' のような名前になる)
internal sealed class AnonymousType<TName, TAge>
{
    private readonly TName _name;
    private readonly TAge _age;

    public TName Name => _name;
    public TAge Age => _age;

    public AnonymousType(TName name, TAge age)
    {
        _name = name;
        _age = age;
    }
    // ToString, Equals, GetHashCode も自動生成される
}

同じアセンブリ内で同じプロパティ名・同じ型・同じ順番の匿名型は、コンパイラが同一の生成クラスを再利用します。

var a = new { Name = "Alice", Age = 30 };
var b = new { Name = "Bob",   Age = 25 };

// a と b は同じ型(プロパティ名・型・順番が一致するため)
Console.WriteLine(a.GetType() == b.GetType()); // True

プロパティの順番が違うと別の型になります。

var c = new { Age = 30, Name = "Carol" }; // 順番が違う

Console.WriteLine(a.GetType() == c.GetType()); // False

ToString と GetHashCode の実装

匿名型のコンパイラ生成クラスは ToString、GetHashCode、Equals を自動でオーバーライドします。

ToString

ToString はすべてのプロパティ名と値を含む文字列を返します。

var product = new { Name = "Widget", Price = 9.99m, InStock = true };

Console.WriteLine(product.ToString());
// { Name = Widget, Price = 9.99, InStock = True }

デバッグ時にオブジェクトの中身をすぐ確認できるため、便利です。

GetHashCode

GetHashCode はすべてのプロパティの値を組み合わせたハッシュを返します。同じプロパティ値を持つ匿名型のインスタンスは同じハッシュコードを返します。

var p1 = new { Name = "Alice", Age = 30 };
var p2 = new { Name = "Alice", Age = 30 };

Console.WriteLine(p1.GetHashCode() == p2.GetHashCode()); // True(値が同じ)

匿名型の等価性

匿名型の Equals は値ベースの比較を行います。すべてのプロパティが等しければ true を返します。これは通常のクラスの参照比較とは異なります。

var p1 = new { Name = "Alice", Age = 30 };
var p2 = new { Name = "Alice", Age = 30 };
var p3 = new { Name = "Bob",   Age = 25 };

// Equals は値で比較
Console.WriteLine(p1.Equals(p2)); // True
Console.WriteLine(p1.Equals(p3)); // False

// == 演算子は参照比較のまま(object から継承)
Console.WriteLine(p1 == p2); // False(異なるインスタンス)

== は匿名型に対してオーバーロードされないため、参照比較のままです。値を比較したい場合は必ず Equals を使います。

// LINQ での活用例: Distinct で重複を除去する
var items = new[]
{
    new { Category = "Food",  Tag = "A" },
    new { Category = "Food",  Tag = "A" }, // 重複
    new { Category = "Drink", Tag = "B" },
};

foreach (var item in items.Distinct())
    Console.WriteLine(item);
// { Category = Food, Tag = A }
// { Category = Drink, Tag = B }

Distinct は内部で Equals を使うため、匿名型の重複除去が自然に機能します。

匿名型を含む匿名型

匿名型のプロパティに別の匿名型を入れることで、ネストした構造を作れます。

var order = new
{
    OrderId = 1001,
    Customer = new
    {
        Name    = "Alice",
        Address = new
        {
            City    = "Tokyo",
            Country = "Japan"
        }
    },
    TotalAmount = 5400m
};

Console.WriteLine(order.OrderId);                   // 1001
Console.WriteLine(order.Customer.Name);             // Alice
Console.WriteLine(order.Customer.Address.City);     // Tokyo
Console.WriteLine(order.Customer.Address.Country);  // Japan
Console.WriteLine(order.TotalAmount);               // 5400

ToString も自動的にネスト構造を展開してくれます。

Console.WriteLine(order);
// { OrderId = 1001, Customer = { Name = Alice, Address = { City = Tokyo, Country = Japan } }, TotalAmount = 5400 }

LINQ でのネスト活用例

匿名型のネストは LINQ の select 句で結果を整形するときによく使われます。

var products = new[]
{
    new { Name = "Apple",  Price = 100, Category = "Fruit"    },
    new { Name = "Milk",   Price = 200, Category = "Drink"    },
    new { Name = "Banana", Price = 80,  Category = "Fruit"    },
    new { Name = "Water",  Price = 50,  Category = "Drink"    },
};

var result = products
    .Where(p => p.Price >= 100)
    .Select(p => new
    {
        p.Name,
        p.Price,
        Info = new
        {
            p.Category,
            IsPremium = p.Price >= 150
        }
    });

foreach (var item in result)
    Console.WriteLine(item);
// { Name = Apple, Price = 100, Info = { Category = Fruit, IsPremium = False } }
// { Name = Milk, Price = 200, Info = { Category = Drink, IsPremium = True } }

まとめ

トピック ポイント
定義 new { Name = 値, ... } で作成。変数は var で受け取る
内部表現 コンパイラが sealed class を自動生成。同じ構造は同じ型を再利用
ToString / GetHashCode すべてのプロパティを使って自動生成される
等価性 Equals は値ベース比較。== は参照比較のまま
ネスト 匿名型のプロパティに別の匿名型を使える

匿名型は LINQ の select 句での結果整形や、メソッド内の一時的なデータ構造として便利です。ただし、メソッドの戻り値としては使えない(型名がないため)ので、複数のメソッドで共有したいデータは名前付きのクラスや record として定義するのが適切です。

C# .NET 匿名型 Anonymous Types Var LINQ
← [C#] 拡張メソッド(Extension Methods)の使い方 [C#] ポインター型(Pointer Types)入門 ① — unsafe コードの基本と * / & 演算子 →

Related Posts

  • [C#] LINQ to Objects を理解する: クエリの内部表現 Jun 22, 2026
  • [C#] LINQ to Objects を理解する: ページング・集合演算・集計 Jun 21, 2026
  • [C#] LINQ to Objects を理解する: フィルター・射影・並べ替え Jun 20, 2026
  • [C#] LINQ to Objects を理解する: 基本構文と遅延実行 Jun 19, 2026

Table of Contents

  • 匿名型の定義
    • プロジェクション初期化子
    • 読み取り専用
  • 匿名型の内部表現
  • ToString と GetHashCode の実装
    • ToString
    • GetHashCode
  • 匿名型の等価性
  • 匿名型を含む匿名型
    • LINQ でのネスト活用例
  • まとめ

Recent Posts

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

Categories

  • C#120
  • .NET119
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • System.Collections.Generic
  • デリゲート
  • Code Snippet
  • DynamoDB
  • LINQ
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メモリ管理
  • リフレクション
  • DoS
  • Nginx
  • 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.