クラス定義を省略する「匿名型」
C#でデータをまとめるオブジェクトを作成するには、通常、まずclassやstructを定義し、その設計図に基づいてインスタンスを生成します。
しかし、LINQのクエリ結果を受け取る場合や、メソッド内だけで使う一時的なデータ構造のために、わざわざ専用のクラスを定義するのは手間がかかり、コードも肥大化します。
このような場合に役立つのが「匿名型(Anonymous Type)」です。匿名型を使用すると、型を明示的に定義することなく、読み取り専用のプロパティを持つオブジェクトをその場で作成できます。
この記事では、匿名型の基本的な構文と特性、そして主な用途について解説します。
匿名型の基本構文
匿名型は、new演算子とオブジェクト初期化子{ ... }を使用して作成します。この際、クラス名は指定しません(できません)。
var 変数名 = new { プロパティ1 = 値1, プロパティ2 = 値2 };
生成されたオブジェクトの型名はコンパイラによって自動生成されるため、プログラマは知ることができません。そのため、変数を受け取る際は必ず**var(型推論)**を使用する必要があります。
コード例1:基本的な使用方法
書籍情報を表す匿名型オブジェクトを作成する例です。
using System;
public class AnonymousTypeExample
{
public static void Main()
{
// クラスを定義せずに、Title と Price プロパティを持つオブジェクトを作成
var book = new
{
Title = "C# Programming Guide",
Price = 3800,
IsDigital = true
};
// プロパティには通常通りアクセス可能
Console.WriteLine($"タイトル: {book.Title}");
Console.WriteLine($"価格: {book.Price:C}"); // C: 通貨書式
// 匿名型の ToString() は、プロパティの内容を見やすく出力するようオーバーライドされている
Console.WriteLine($"オブジェクトの内容: {book}");
}
}
出力結果:
タイトル: C# Programming Guide
価格: ¥3,800
オブジェクトの内容: { Title = C# Programming Guide, Price = 3800, IsDigital = True }
匿名型の重要な特性
匿名型には、通常のクラスとは異なるいくつかの重要な特性があります。
- プロパティは読み取り専用(ReadOnly): 匿名型のプロパティは、初期化後に値を変更することはできません(Immutable)。C#
// book.Price = 4000; // コンパイルエラー - 型名はコンパイラ生成: 内部的にはクラスが生成されていますが、その名前はコンパイラしか知りません。そのため、メソッドの戻り値や引数として匿名型を(
object型以外で)受け渡すことは困難であり、基本的にはメソッド内のローカル変数として使用します。 - 値による等価性(Value Semantics): 同じプロパティ名、同じ型、同じ値を持つ2つの匿名型インスタンスは、
Equalsメソッドで「等しい」と判定されます。
主な用途:LINQにおける射影(Projection)
匿名型が最も頻繁に使用されるのは、LINQのSelectメソッドを使って、必要なデータだけを抽出(射影)する場合です。
既存のクラスから特定のプロパティだけを抜き出したり、計算結果を含めたりした「新しい形式のデータ」を、クラス定義なしでリスト化できます。
コード例2:LINQでの活用
Productクラスのリストから、在庫切れ商品の「名前」と「ID」だけを持つ軽量なオブジェクトのリストを作成する例です。
using System;
using System.Collections.Generic;
using System.Linq;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public int Stock { get; set; }
public string Description { get; set; } // データ量が多いと仮定
}
public class LinqProjectionExample
{
public static void Main()
{
var products = new List<Product>
{
new Product { Id = 1, Name = "Mouse", Stock = 50, Description = "..." },
new Product { Id = 2, Name = "Keyboard", Stock = 0, Description = "..." },
new Product { Id = 3, Name = "Monitor", Stock = 0, Description = "..." }
};
// 在庫が0の商品を抽出し、必要な情報だけの匿名型に変換(射影)する
var outOfStockItems = products
.Where(p => p.Stock == 0)
.Select(p => new
{
p.Id, // プロパティ名を省略すると元の名前 (Id) が使われる
ProductName = p.Name // 名前を変更して割り当てることも可能
});
Console.WriteLine("--- 在庫切れ商品リスト ---");
foreach (var item in outOfStockItems)
{
// item は匿名型
Console.WriteLine($"ID: {item.Id}, Name: {item.ProductName}");
}
}
}
出力結果:
--- 在庫切れ商品リスト ---
ID: 2, Name: Keyboard
ID: 3, Name: Monitor
補足:with 式による非破壊的変更
C# 9.0以降では、with式を使用することで、既存の匿名型インスタンスを元に、一部のプロパティだけを変更した新しいインスタンスを作成できます。
var config = new { Theme = "Dark", FontSize = 12 };
// Theme だけを "Light" に変更した新しいインスタンスを作成
var newConfig = config with { Theme = "Light" };
Console.WriteLine(config); // { Theme = Dark, FontSize = 12 }
Console.WriteLine(newConfig); // { Theme = Light, FontSize = 12 }
まとめ
匿名型は、クラスを定義する手間を省き、その場限りのデータ構造を扱うための強力な機能です。
var obj = new { Prop1 = val1, ... };で作成します。- プロパティは読み取り専用です。
- 主にLINQクエリで、必要なデータだけを抽出・加工する際に利用されます。
- メソッドの戻り値として使う場合は、タプル(
ValueTuple)の使用も検討すると良いでしょう。
