Mastering Lightweight Data Structures for Flexible Code
C# gives developers many ways to represent data – from full classes and records to structs and tuples.
But sometimes, you need a quick, lightweight object just to hold some data without defining a whole class.
That’s where Anonymous Types come in.
They’re simple, fast to use, and perfect for one-off projections, especially in LINQ queries.
🔹 What Are Anonymous Types?
Anonymous types let you create an object without defining a class.
The compiler automatically creates a type behind the scenes — complete with properties, typing, and read-only behavior.
Example:
var person = new { Name = "Alice", Age = 30, City = "London" };
Console.WriteLine($"{person.Name}, {person.Age}, {person.City}");
Output:
Alice, 30, London
✅ Key Points:
- The
varkeyword is required because the type name is generated automatically. - Properties are read-only (you can’t change them after creation).
- Anonymous types are type-safe — the compiler enforces property names and types.
🔹 Why Use Anonymous Types?
Anonymous types are ideal when you:
- Need quick, temporary data containers
- Want to return or group data from LINQ queries
- Don’t want to create full classes just for display or transformation
- Are working with projections (custom shapes of data)
🔹 Creating Anonymous Types
Anonymous types are defined with the new { } syntax:
var book = new { Title = "1984", Author = "George Orwell", Price = 12.99 };
You can mix strings, numbers, and even nested anonymous types:
var order = new
{
OrderId = 101,
Customer = new { Name = "John", Email = "john@example.com" },
Total = 49.99
};
Accessing properties:
Console.WriteLine(order.Customer.Name); // John
🔹 Type Inference and Property Names
When creating an anonymous type, C# automatically infers the property types.
You can also use existing variables as shorthand.
Example:
string product = "Laptop";
double price = 799.99;
var item = new { product, price };
Console.WriteLine($"{item.product} - £{item.price}");
Output:
Laptop - £799.99
✅ C# automatically names the properties product and price — based on the variable names.
🔹 Anonymous Types in LINQ Queries
Anonymous types are used extensively in LINQ projections to shape query results.
Example:
var students = new[]
{
new { Name = "Alice", Score = 85 },
new { Name = "Bob", Score = 72 },
new { Name = "Clara", Score = 90 }
};
var results =
from s in students
where s.Score > 75
select new
{
Student = s.Name,
Grade = s.Score >= 85 ? "A" : "B"
};
foreach (var r in results)
Console.WriteLine($"{r.Student}: {r.Grade}");
Output:
Alice: A
Clara: A
✅ What’s Happening:
select new { ... }creates an anonymous type per result- Each object has
StudentandGradeproperties - No class definition needed — it’s generated automatically
🔹 Anonymous Types Are Immutable
All properties of anonymous types are read-only.
This ensures safety when used in queries or projections.
Example:
var car = new { Make = "Tesla", Model = "Model 3" };
// car.Make = "Ford"; ❌ Error: Property is read-only
🔧 Tip:
If you need mutable objects, define a small class or record instead.
🔹 Nested Anonymous Types
You can even nest anonymous types to structure data hierarchically:
var employee = new
{
Name = "Sarah",
Address = new { Street = "12 High St", City = "Bristol" },
Skills = new[] { "C#", "SQL", "Azure" }
};
Console.WriteLine($"{employee.Name} from {employee.Address.City}");
Output:
Sarah from Bristol
🔹 Limitations of Anonymous Types
While convenient, anonymous types have a few restrictions:
| Limitation | Description |
|---|---|
| Read-only | Properties cannot be changed after creation |
| Scope-limited | Type exists only within the method or project assembly |
| No explicit type name | You can’t specify it as a return type or class property |
| Not suited for APIs | Shouldn’t be used for public interfaces or serialization |
✅ Use them for: internal data transformations, temporary grouping, or UI display logic.
❌ Avoid them for: data models, DTOs, or API responses.
🧩 Real-World Example – Combining Anonymous Types and LINQ
var products = new[]
{
new { Name = "Laptop", Price = 1200 },
new { Name = "Mouse", Price = 25 },
new { Name = "Keyboard", Price = 45 },
new { Name = "Monitor", Price = 200 }
};
var summary =
from p in products
where p.Price > 30
orderby p.Price descending
select new
{
Product = p.Name,
Discounted = p.Price * 0.9,
Category = p.Price > 500 ? "Premium" : "Standard"
};
foreach (var s in summary)
Console.WriteLine($"{s.Product} - £{s.Discounted} ({s.Category})");
Output:
Laptop - £1080 (Premium)
Monitor - £180 (Standard)
Keyboard - £40.5 (Standard)
📚 Summary
| Concept | Description | Example |
|---|---|---|
| Anonymous Type | Object created without defining a class | new { Name = "James", Age = 59 } |
| Property Access | Access fields with dot notation | person.Name |
| Immutability | Properties can’t be changed | Read-only by design |
| LINQ Use | Shape query results | select new { p.Name, p.Price } |
✅ Best Practices
- Use anonymous types for short-lived, internal data representations
- Avoid using them as method parameters or return types
- Keep property names clear and descriptive
- Use
select new { ... }to make LINQ queries cleaner - For complex data models, use records or classes instead
🧪 Challenge Task
Create a LINQ query that produces an anonymous type for each employee:
- Use a
wherefilter to include only those with Salary > £40,000 - Use
select new { Name, Department, AnnualTax = Salary * 0.2 } - Print out results as:
“James (IT) pays £12,000 tax.”
Example dataset:
var employees = new[]
{
new { Name = "James", Department = "IT", Salary = 60000 },
new { Name = "Anna", Department = "HR", Salary = 38000 },
new { Name = "Liam", Department = "Finance", Salary = 45000 }
};
👨💻 Want More?
Learn more C# LINQ on our Advanced C# Course covers:
- Anonymous types and projections
- Tuples, records, and data shaping
- Query comprehension and transformation
- Real-world exercises with LINQ to Objects and SQL
Build confidence writing clear, type-safe, and expressive C# code — without unnecessary boilerplate.