C# Anonymous Types
Introduction
Anonymous types in C# provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first. Introduced in C# 3.0, anonymous types are particularly useful when you need to create simple data structures on the fly, especially in LINQ queries.
Anonymous types allow you to create objects with strongly-typed properties without writing a formal class definition. The compiler generates the class for you behind the scenes, including appropriate methods like Equals()
, GetHashCode()
, and ToString()
.
Basic Syntax
The syntax for creating an anonymous type uses the new
keyword followed by curly braces containing property name/value pairs.
var person = new { Name = "John", Age = 30 };
// Accessing properties
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
Output:
Name: John, Age: 30
In this example, we've created an anonymous type with two properties: Name
and Age
. The compiler infers the property types based on the assigned values (string
for Name and int
for Age).
How Anonymous Types Work
Type Inference
When you create an anonymous type, the C# compiler:
- Generates a class with the specified properties
- Creates appropriate constructors
- Implements
Equals()
,GetHashCode()
, andToString()
- Makes all properties
public
andread-only
var product = new { Id = 101, Name = "Laptop", Price = 999.99m };
Console.WriteLine(product.GetType().ToString());
Output:
<>f__AnonymousType0`3[System.Int32,System.String,System.Decimal]
The output shows this is an anonymous type with three properties of types int
, string
, and decimal
.
Property Name Inference
You can also infer property names from variables:
string name = "Alice";
int age = 25;
var person = new { name, age }; // Same as new { name = name, age = age }
Console.WriteLine($"{person.name} is {person.age} years old");
Output:
Alice is 25 years old
Equality Comparison
Two anonymous objects with the same property names, types, and values are considered equal:
var obj1 = new { Name = "John", Age = 30 };
var obj2 = new { Name = "John", Age = 30 };
var obj3 = new { Name = "Jane", Age = 30 };
Console.WriteLine($"obj1 equals obj2: {obj1.Equals(obj2)}");
Console.WriteLine($"obj1 equals obj3: {obj1.Equals(obj3)}");
Output:
obj1 equals obj2: True
obj1 equals obj3: False
Practical Applications
LINQ Queries
Anonymous types are frequently used in LINQ queries for projecting or transforming data:
List<string> fruits = new List<string> { "Apple", "Banana", "Cherry", "Date", "Elderberry" };
var fruitData = fruits.Select(f => new {
Name = f,
Length = f.Length,
FirstLetter = f[0]
});
foreach (var item in fruitData)
{
Console.WriteLine($"{item.Name}: {item.Length} characters, starts with '{item.FirstLetter}'");
}
Output:
Apple: 5 characters, starts with 'A'
Banana: 6 characters, starts with 'B'
Cherry: 6 characters, starts with 'C'
Date: 4 characters, starts with 'D'
Elderberry: 10 characters, starts with 'E'
Joining Multiple Data Sources
Anonymous types can combine data from different sources:
var students = new[] {
new { Id = 1, Name = "Alice" },
new { Id = 2, Name = "Bob" },
new { Id = 3, Name = "Charlie" }
};
var grades = new[] {
new { StudentId = 1, Course = "Math", Score = 90 },
new { StudentId = 2, Course = "Math", Score = 85 },
new { StudentId = 1, Course = "Science", Score = 95 },
new { StudentId = 3, Course = "Math", Score = 88 }
};
var query = from student in students
join grade in grades
on student.Id equals grade.StudentId
select new {
student.Name,
grade.Course,
grade.Score
};
foreach (var item in query)
{
Console.WriteLine($"{item.Name} scored {item.Score} in {item.Course}");
}
Output:
Alice scored 90 in Math
Alice scored 95 in Science
Bob scored 85 in Math
Charlie scored 88 in Math
Temporary Data Structures
Anonymous types are useful for creating temporary data structures without formal class definitions:
public static void DisplayPersonSummary(string name, int age, string city)
{
// Create temporary anonymous type to hold data
var personData = new {
FullName = name,
Age = age,
Location = city,
IsAdult = age >= 18
};
Console.WriteLine($"{personData.FullName} is {personData.Age} years old");
Console.WriteLine($"Lives in: {personData.Location}");
Console.WriteLine($"Adult status: {(personData.IsAdult ? "Adult" : "Minor")}");
}
// Usage
DisplayPersonSummary("David Smith", 22, "New York");
Output:
David Smith is 22 years old
Lives in: New York
Adult status: Adult
Limitations of Anonymous Types
While convenient, anonymous types have some limitations:
- Read-only: Properties cannot be modified after creation
- Limited scope: Anonymous types work best for local use
- Cannot be used as method return types or parameters: Since they don't have a named type
- Cannot define methods: Only properties are allowed
var person = new { Name = "John", Age = 30 };
// person.Age = 31; // Error: Cannot modify the read-only property
// Workaround: create a new object with updated values
person = new { Name = person.Name, Age = 31 };
Console.WriteLine($"Updated age: {person.Age}");
Output:
Updated age: 31
Best Practices
-
Use anonymous types for temporary, local data structures or projections in LINQ queries.
-
Create proper classes for more complex objects or when you need to pass data across method boundaries.
-
Consider using records (C# 9.0+) or tuples as alternatives for simple data structures that need to be passed between methods.
// Alternative using a tuple
(string Name, int Age) GetPersonInfo()
{
return ("John", 30);
}
// Alternative using a record (C# 9.0+)
record Person(string Name, int Age);
// Usage
var p = new Person("John", 30);
- Leverage property name inference when appropriate to make code more concise.
// Instead of this:
var book = new { Title = title, Author = author, Year = year };
// Use this:
var book = new { title, author, year };
Summary
Anonymous types in C# provide a convenient way to create temporary data objects without explicitly defining classes. They're particularly useful in LINQ queries, for data projections, and when you need simple data containers with minimal setup.
Key points to remember:
- Created using the syntax
new { Property1 = value1, Property2 = value2 }
- Properties are read-only and strongly typed
- Great for LINQ queries and temporary data structures
- Have limitations that make them unsuitable for cross-method communication
- The compiler generates
Equals()
,GetHashCode()
, andToString()
methods
Exercises
-
Create a LINQ query using anonymous types to find all words in a list that are longer than 5 characters and return their original value, length, and whether they start with a vowel.
-
Use anonymous types to join data from two lists: a list of products (with ID, name, and price) and a list of categories (with ID and name). Create a projection that includes the product name, price, and category name.
-
Create a method that accepts a list of integers and returns the statistics (min, max, average, sum) using an anonymous type.
Additional Resources
- Microsoft Documentation on Anonymous Types
- C# LINQ Documentation
- C# Records - An alternative to anonymous types for some use cases
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)