Back to Blog
C#

C# Anonymous Type Properties: Syntax and Behavior

C# anonymous type properties: Learn how to declare them, their read-only behavior, equality semantics, LINQ usage, and practical limitations.

C#Anonymous TypesLINQType SystemCompiler
Illustration of C# anonymous type properties showing a compiler-generated type with read-only properties.

Anonymous types in C# provide a convenient way to create an object with a set of read-only properties without writing a named class. The compiler generates a type behind the scenes, and the property types and names are inferred from the initializer expression. This article explains how anonymous type properties behave, how to use them effectively, and where they fall short.

How Anonymous Type Properties Are Declared

The syntax for an anonymous type uses the new keyword followed by an object initializer with property assignments. Each property can be assigned an explicit name or inferred from the variable name.

var person = new { Name = "Alice", Age = 30 }; var fromVariables = new { name, age }; // property names become "name" and "age"

The compiler infers the property type from the expression. If you use a variable, the property name matches the variable name unless you assign an explicit name. The properties are read-only: you cannot assign to them after creation.

Read-Only Nature and Compiler-Generated Types

Every anonymous type property is read-only. The compiler generates a class with internal accessibility, and the properties have only getters. This means the object is immutable after construction. The compiler also overrides ToString to produce a readable representation and overrides Equals and GetHashCode to provide value-based equality.

var a = new { X = 1, Y = 2 }; var b = new { X = 1, Y = 2 }; Console.WriteLine(a.Equals(b)); // True

Because the type is compiler-generated, you cannot write its name in source code. To use the properties directly, declare the variable with var or rely on type inference in expressions such as LINQ projections. Since you cannot name the type, you cannot use it explicitly in method signatures or field declarations.

Using Anonymous Types in LINQ Queries

Anonymous types are commonly used in LINQ projections to shape query results. You can select a subset of properties from an entity without creating a named DTO.

var result = from p in products select new { p.Name, p.Price };

The resulting sequence contains objects with Name and Price properties. This keeps the query concise and avoids defining a separate class for the projection.

Limitations: When Anonymous Types Fall Short

The main limitation is that you cannot name an anonymous type in source code. It cannot be used explicitly as a method parameter, return type, or class field. If you need to pass the object around with its typed shape, use a named type or a tuple. Additionally, the generated type is internal, so anonymous types cannot be used in a public API across assembly boundaries.

Another limitation is that you cannot add custom methods to an anonymous type; you get only the defined properties and compiler-generated members. If you need custom behavior, use a named class.

Equality and Hashing Behavior

The compiler-generated Equals method compares each property value, and GetHashCode computes a hash from those values. This means two instances of the same anonymous type with the same property values are equal. This is useful in LINQ operations like Distinct or Union, but it also means that equality depends on the property values, not reference identity.

var one = new { Value = 10 }; var two = new { Value = 10 }; Console.WriteLine(one == two); // False, because == is reference equality Console.WriteLine(one.Equals(two)); // True

Note that == is not overridden; only Equals is. So you should use Equals when comparing anonymous objects.

Practical Considerations for Maintainability and Performance

Using anonymous types can reduce boilerplate, but it can also obscure the shape of data. When the same projection is used in multiple places, a named type improves clarity and allows reuse. At runtime, an anonymous type is an ordinary compiler-generated class, and property access is direct. However, reflection-based inspection of anonymous types adds overhead, so avoid reflection-heavy scenarios.

Alternatives: Named Types, Tuples, and Records

When you need a type that can be returned from a method or stored in a field, consider using a named class, a named tuple, or a C# record. Tuples provide lightweight data carriers, but without named elements they expose Item1, Item2, and so on. Records offer value equality and are more expressive for DTOs.

public record Person(string Name, int Age);

Records are a better fit when you need a reusable type with value semantics. Anonymous types remain useful for quick, local projections where a named type would be overkill.

C# Anonymous Type Properties: Syntax, Behavior, and LINQ | RYUSLOG DEV