Skip to main content

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.

csharp
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:

  1. Generates a class with the specified properties
  2. Creates appropriate constructors
  3. Implements Equals(), GetHashCode(), and ToString()
  4. Makes all properties public and read-only
csharp
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:

csharp
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:

csharp
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:

csharp
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:

csharp
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:

csharp
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:

  1. Read-only: Properties cannot be modified after creation
  2. Limited scope: Anonymous types work best for local use
  3. Cannot be used as method return types or parameters: Since they don't have a named type
  4. Cannot define methods: Only properties are allowed
csharp
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

  1. Use anonymous types for temporary, local data structures or projections in LINQ queries.

  2. Create proper classes for more complex objects or when you need to pass data across method boundaries.

  3. Consider using records (C# 9.0+) or tuples as alternatives for simple data structures that need to be passed between methods.

csharp
// 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);
  1. Leverage property name inference when appropriate to make code more concise.
csharp
// 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(), and ToString() methods

Exercises

  1. 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.

  2. 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.

  3. Create a method that accepts a list of integers and returns the statistics (min, max, average, sum) using an anonymous type.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)