C# Anonymous Type: Syntax, Usage, and Limitations
c# anonymous type: Learn how to use C# anonymous types for read-only data projections, their syntax, runtime behavior, and when to prefer tuples or records.
The C# anonymous type feature lets you create a read-only object with inferred property names and types without declaring a named class. You typically use it in LINQ projections or when you need a temporary shape for data that does not warrant a full type definition. The compiler generates a sealed internal type behind the scenes, and the resulting object is immutable. Here is the basic syntax:
var person = new { Name = "Alice", Age = 30 }; Console.WriteLine($"{person.Name} is {person.Age} years old");
The var keyword is required because the type has no name you can write. The compiler infers the property names from the initializer expressions, and the property types from the values assigned. If you use a simple member access like person.Name, the property name is taken from that member. If you use an expression, you can provide an explicit name with the Name = expression syntax.
Declaring an Anonymous Type with Object Initializers
Anonymous types are created with the new keyword followed by an object initializer that contains one or more property assignments. The compiler treats each assignment as a read-only property. For example:
var product = new { Id = 1, Description = "Laptop", Price = 1200.00m };
Here the anonymous type has three properties: Id of type int, Description of type string, and Price of type decimal. The property names are taken directly from the left side of each assignment. If you omit the name and use a variable or member access, the compiler uses the source member name:
string city = "Seattle"; var location = new { city, State = "WA" };
In this case, the property is named city because that is the variable name. The State property is explicitly named. This shorthand works with fields, properties, and local variables.
How the Compiler Generates the Type
When you write an anonymous type, the C# compiler generates a sealed internal class that inherits directly from System.Object. The class contains only read-only properties and a constructor that sets them. The compiler also overrides Equals, GetHashCode, and ToString so that two instances with the same property values compare equal and produce a readable string representation.
Two anonymous types in the same assembly that have identical property names and types in the same order are unified by the compiler. That means they share the same generated type. This unification matters when you use anonymous types in a method body and then pass them to another method via object or dynamic. It also affects how Equals behaves across different anonymous type instances.
The generated type is internal, so it is not accessible outside the assembly. This is one reason you cannot use an anonymous type as a return type or a parameter type in a public method. If you need to pass such data across method boundaries, you must use a named type, a tuple, or a record.
Using Anonymous Types in LINQ Projections
Anonymous types are most common in LINQ queries where you want to select a subset of fields from a collection. The Select method can project each source element into an anonymous object, giving you a shape tailored to the current query without defining a separate class.
var people = new[] { new { Name = "Alice", Age = 30, City = "Seattle" }, new { Name = "Bob", Age = 25, City = "Portland" } }; var namesAndCities = people .Where(p => p.Age > 26) .Select(p => new { p.Name, p.City }); foreach (var item in namesAndCities) { Console.WriteLine($"{item.Name} lives in {item.City}"); }
The Select projection creates a new anonymous type with Name and City properties. Because the source is an array of anonymous types, the query works without any explicit type declaration. This pattern is particularly useful in Entity Framework and other ORM queries where you want to fetch only the columns you need, reducing the amount of data transferred from the database.
When you use anonymous types in LINQ to Objects, the query is executed eagerly or lazily depending on the operator. The projection itself does not introduce any runtime cost beyond the allocation of the new anonymous type objects. In LINQ to SQL or Entity Framework, the projection is translated into a SQL SELECT that returns only the requested columns, which can improve query performance by reducing network payload and database load.
Read-Only Properties and Immutability
Every property of an anonymous type is read-only. There is no setter, so you cannot modify the value after the object is created. This immutability is intentional: it makes the type safe to share across threads and ensures that the data captured in a LINQ query remains stable during deferred execution.
Because the properties are read-only, you cannot use an anonymous type as a mutable data holder. If you need to change a value, you must create a new instance. This is similar to how System.Tuple works, but unlike ValueTuple, which has mutable fields by default.
The read-only nature also means that anonymous types are safe to use as keys in a Dictionary or HashSet because their GetHashCode and Equals implementations are based on the property values. Two anonymous objects with the same property values are considered equal, which is useful when you need to group or deduplicate data.
Anonymous Types vs Tuples: Which to Choose?
Tuples, specifically ValueTuple, provide an alternative when you need a lightweight data structure with named or unnamed fields. Unlike anonymous types, tuples are value types, and their fields are mutable by default. You can also use tuples as method return types and parameters, which is not possible with anonymous types.
| Criterion | Anonymous Type | ValueTuple |
|---|---|---|
| Type kind | Reference type (class) | Value type (struct) |
| Mutability | Read-only properties | Mutable fields by default |
| Named fields | Yes, inferred or explicit | Yes, via Item1 or custom names |
| Use as return type | No (cannot name the type) | Yes |
| Equality semantics | Value-based via overridden Equals | Value-based via Equals and == (C# 7.3+) |
| Typical use | LINQ projections, local shapes | Method returns, deconstruction |
Choose an anonymous type when you are working within a single method or a LINQ query and you do not need to pass the result to another method. Choose a ValueTuple when you need to return multiple values from a method or when you need mutable fields. If you need a named, reusable type with value equality, consider a record instead.
Performance and Memory Allocation
Anonymous types are reference types, so creating one allocates an object on the heap. The compiler generates a constructor that assigns each property, and the ToString, GetHashCode, and Equals methods are overridden. For small numbers of objects, the allocation cost is negligible. However, in hot paths where you create thousands of anonymous objects per second, the overhead can become noticeable.
Each anonymous type instance requires memory for the object header, the property fields, and the method table pointer. The exact size depends on the number and type of properties. Because the type is sealed and internal, the JIT can sometimes optimize method calls, but the allocation itself is unavoidable.
If you are using anonymous types in a LINQ query over a large collection, the projection creates one new object per element. This is similar to the cost of creating any other reference type. If you need to minimize allocations, you could use a ValueTuple or a mutable struct, but that changes the semantics and may not be worth the complexity unless profiling shows a real bottleneck.
Another consideration is that the compiler generates a new type for each unique property signature. If you create many different anonymous types in the same assembly, the generated code grows, but the impact is usually minor. The JIT compiler may also need to compile each generated type's methods the first time they are used, which adds a small one-time cost.
When to Prefer Records or Named Types
Anonymous types are designed for short-lived, local use. They are not suitable for public APIs, because the type is internal and cannot be named. If you need to return a projected shape from a method, you have several options:
- Define a named class or struct with the required properties.
- Use a
ValueTuplewith named elements. - Use a
record(C# 9+) for an immutable reference type with value equality.
Records are particularly useful when you want a read-only data holder that can be passed across method boundaries and compared by value. Unlike anonymous types, records can be used as return types, and they support inheritance and pattern matching. For example:
public record PersonInfo(string Name, int Age); public PersonInfo GetPersonInfo() { return new PersonInfo("Alice", 30); }
If you are writing a library or a large application where the shape of the data is part of the contract, a named type is almost always better. Anonymous types should stay inside method bodies or LINQ expressions where the scope is clear and the type is not exposed.
One edge case to be aware of is that anonymous types are not compatible across assemblies. If you create an anonymous type in one assembly and try to pass it to another, the types will not match even if the property names and types are identical. This is because the generated type is internal to each assembly. If you need cross-assembly data transfer, use a named type or a tuple.