Projection, Anonymous Types, and Select
The first LINQ method we'll dive into is Select
.
Select
is used to transform one type into another, essentially making something out of something else.- It is used by LINQ for projection, where you can change the shape of the data you’re working with.
What Does Select
Do?
- It takes each element in a collection, calls the transformer function, and returns the resulting elements.
- Common transformations include constructing new objects, modifying data, or projecting specific properties.
Example
Let’s say you have a collection of Employee
objects, and you want to project their Name
and Salary
properties into a new type. Here’s an example:
var employeeData = employees.Select(e => new {
e.Name,
e.Salary
});
Wait, what the heck is that thing? We're using new
without a type? They're known as anonymous types, and they're supremely useful.
Anonymous Types
Anonymous types are types that have no defined class.
They are types that are created dynamically, allowing you to group multiple properties together without declaring an explicit type for the data.
Anonymous types have a few unique features.
- The properties of anonymous types are read-only.
- Anonymous types automatically implement
Equals
andGetHashCode
based on their structural properties, meaning two anonymous objects with the same values are considered equal. (Exactly like records in fact. You could say that anonymous types were the original record types in C#.)
var employee1 = new { Name = "John", Salary = 120000 };
var employee2 = new { Name = "John", Salary = 120000 };
Console.WriteLine(employee1.Equals(employee2)); // Output: True
Why is this useful? Why not just use the original type as is?
If you have a complex type with lots of nested data, you can project a new type to "flatten" the data, like so:
var employeeData = employees.Select(e => new {
ManagerName = e.Manager.Name,
e.Salary,
DepartmentName = e.Department.Name
});
You might need a completely different object from your first type:
var employeeData = employees.Select(e => new EmployeeInfo(e));
Spoiler alert, in the next course we'll be using LINQ to query databases, and we can improve performance by projecting a new type that has fewer columns.
When to Use Anonymous Types
- Use them when you need a quick data projection without creating new classes.
- Typically used in temporary situations like LINQ queries where the projected result is not intended to last beyond its immediate use.
Browse Source Code
You can browse the source code of LINQ to see how Select is implemented. LINQ methods, including Select, are built on top of extension methods and leverage IEnumerable
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
foreach (TSource element in source)
{
yield return selector(element);
}
}
Things to Avoid
Avoid using Select
as a kind of foreach
loop: Although it may seem tempting, Select
is not a substitute for foreach
. Select
should be used for projection (i.e., transforming data), not for performing actions or side effects.
//Incorrect Usage
employees.Select(e => Console.WriteLine(e.Name)); //🤢
This misuses Select as if it were foreach
. If you need to iterate over a collection to perform an action, use foreach
instead.
Another common mistake is using Select to modify the objects that are being iterated over. Select should be used to transform and project new objects, not mutate existing ones.
//Incorrect Usage
employees.Select(e => e.Salary += 5000); //🤢
This example tries to modify each employee’s salary, which is not the intended purpose of Select. If you need to mutate objects, handle that logic elsewhere.