How to Project Anonymous Types in C# LINQ

Projecting anonymous types in C# LINQ enables developers to transform query results into lightweight, read-only objects without explicitly defining a class or struct. This article explains how to use the LINQ select clause to construct anonymous types using both query and method syntax, assign custom property names, and handle common limitations.


What Is an Anonymous Type Projection?

An anonymous type projection uses the new keyword followed by an object initializer within a LINQ select clause. The C# compiler automatically generates an immutable class containing read-only properties inferred from your assignment.

Query Syntax

In LINQ query syntax, place the new { ... } expression directly inside the select clause at the end of your query.

var employees = new List<Employee>
{
    new Employee { Id = 1, FirstName = "Jane", LastName = "Doe", Salary = 85000, Department = "IT" },
    new Employee { Id = 2, FirstName = "John", LastName = "Smith", Salary = 60000, Department = "HR" }
};

var query = from e in employees
            where e.Salary > 70000
            select new
            {
                FullName = e.FirstName + " " + e.LastName,
                e.Department
            };

foreach (var item in query)
{
    Console.WriteLine($"{item.FullName} works in {item.Department}");
}

Method Syntax

In method syntax, supply a lambda expression returning the anonymous type directly into the Select() extension method.

var result = employees
    .Where(e => e.Salary > 70000)
    .Select(e => new
    {
        FullName = $"{e.FirstName} {e.LastName}",
        e.Department
    });

Property Name Rules

When projecting anonymous types, property names can be inferred or explicitly assigned:

  1. Inferred Names: If you pass an existing member without assigning a name, the compiler uses the existing member's name (projection initializers).

    select new { e.FirstName, e.Department }
    // Results in properties: item.FirstName, item.Department
  2. Explicit Names: If you perform a transformation, calculation, or want a different property name, you must explicitly assign it.

    select new { DisplayName = e.FirstName, AnnualComp = e.Salary }
    // Results in properties: item.DisplayName, item.AnnualComp

Working with Nested Anonymous Types

You can nest anonymous types within each other to represent hierarchical data structures.

var nestedResult = employees.Select(e => new
{
    e.Id,
    Details = new
    {
        e.FirstName,
        e.LastName
    },
    Compensation = new
    {
        e.Salary,
        Bonus = e.Salary * 0.10m
    }
});

Key Considerations and Limitations

  • Immutability: Properties on anonymous types are read-only and cannot be modified after instantiation.
  • Scope: Anonymous types are designed for local use within the method they are declared in. They cannot be used as method return types or parameters without boxing them to object or using dynamic, which loses compile-time type safety.
  • Alternative for Method Returns: If you need to pass projected data outside the declaring method, use C# 7+ value tuples ((string Name, decimal Salary)) or C# 9+ record types instead of anonymous types.