LINQ (Language Integrated Query) is a powerful feature in C# that provides a consistent way to query data from various sources such as collections, databases, XML documents, and more. LINQ allows you to write queries directly in C#, utilizing the language's syntax and type checking, resulting in more readable and maintainable code.
LINQ provides a uniform query syntax to retrieve and manipulate data from different sources. It integrates querying capabilities into the C# language, allowing you to work with data in a more intuitive and type-safe manner.
Basic LINQ Query:
namespace LinqExamples;
class Program
{
static void Main(string[] args)
{
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
}
}
LINQ queries can be written using two different syntaxes: query syntax and method syntax. Both syntaxes achieve the same results but offer different approaches to writing queries.
Query Syntax:
namespace LinqExamples;
class Program
{
static void Main(string[] args)
{
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
}
}
namespace LinqExamples;
class Program
{
static void Main(string[] args)
{
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
}
}
LINQ provides a wide range of methods to perform operations such as filtering, projecting, joining, grouping, and more. Here is a table of some commonly used LINQ methods along with the versions in which they were introduced:
Method | Description | Version |
---|---|---|
Where | Filters a sequence of values based on a predicate. | C# 3.0 |
Select | Projects each element of a sequence into a new form. | C# 3.0 |
SelectMany | Projects each element of a sequence to an IEnumerable<T> and flattens the resulting sequences. | C# 3.0 |
OrderBy | Sorts the elements of a sequence in ascending order. | C# 3.0 |
OrderByDescending | Sorts the elements of a sequence in descending order. | C# 3.0 |
ThenBy | Performs a subsequent ordering of the elements in a sequence in ascending order. | C# 3.0 |
ThenByDescending | Performs a subsequent ordering of the elements in a sequence in descending order. | C# 3.0 |
GroupBy | Groups the elements of a sequence according to a specified key selector function. | C# 3.0 |
ToDictionary | Creates a Dictionary<TKey,TValue> from an IEnumerable<T>. | C# 3.0 |
ToList | Creates a List<T> from an IEnumerable<T>. | C# 3.0 |
ToArray | Creates an array from an IEnumerable<T>. | C# 3.0 |
First | Returns the first element of a sequence. | C# 3.0 |
FirstOrDefault | Returns the first element of a sequence, or a default value if no element is found. | C# 3.0 |
Last | Returns the last element of a sequence. | C# 3.0 |
LastOrDefault | Returns the last element of a sequence, or a default value if no element is found. | C# 3.0 |
Single | Returns the only element of a sequence, and throws an exception if there is not exactly one element. | C# 3.0 |
SingleOrDefault | Returns the only element of a sequence, or a default value if no element is found. | C# 3.0 |
Any | Determines whether any element of a sequence satisfies a condition. | C# 3.0 |
All | Determines whether all elements of a sequence satisfy a condition. | C# 3.0 |
Count | Returns the number of elements in a sequence. | C# 3.0 |
Sum | Computes the sum of a sequence of numeric values. | C# 3.0 |
Min | Returns the minimum value in a sequence of values. | C# 3.0 |
Max | Returns the maximum value in a sequence of values. | C# 3.0 |
Average | Computes the average of a sequence of numeric values. | C# 3.0 |
Aggregate | Applies an accumulator function over a sequence. | C# 3.0 |
Contains | Determines whether a sequence contains a specified element. | C# 3.0 |
Distinct | Returns distinct elements from a sequence. | C# 3.0 |
Except | Produces the set difference of two sequences. | C# 3.0 |
Intersect | Produces the set intersection of two sequences. | C# 3.0 |
Union | Produces the set union of two sequences. | C# 3.0 |
Join | Correlates the elements of two sequences based on matching keys. | C# 3.0 |
GroupJoin | Correlates the elements of two sequences based on key equality and groups the results. | C# 3.0 |
Zip | Applies a specified function to the corresponding elements of two sequences. | C# 4.0 |
Skip | Bypasses a specified number of elements in a sequence and then returns the remaining elements. | C# 3.0 |
SkipWhile | Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. | C# 3.0 |
Take | Returns a specified number of contiguous elements from the start of a sequence. | C# 3.0 |
TakeWhile | Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements. | C# 3.0 |
Concat | Concatenates two sequences. | C# 3.0 |
DefaultIfEmpty | Returns the elements of the specified sequence or the type parameter's default value in a singleton collection if the sequence is empty. | C# 3.0 |
ElementAt | Returns the element at a specified index in a sequence. | C# 3.0 |
ElementAtOrDefault | Returns the element at a specified index in a sequence or a default value if the index is out of range. | C# 3.0 |
Range | Generates a sequence of integral numbers within a specified range. | C# 3.0 |
Repeat | Generates a sequence that contains one repeated value. | C# 3.0 |
Empty | Returns an empty IEnumerable<T> that has the specified type argument. | C# 3.0 |
AsEnumerable | Returns the input typed as IEnumerable<T>. | C# 3.0 |
AsQueryable | Converts an IEnumerable to an IQueryable. | C# 3.0 |
Cast | Casts the elements of an IEnumerable to the specified type. | C# 3.0 |
OfType | Filters the elements of an IEnumerable based on a specified type. | C# 3.0 |
Append | Appends a value to the end of the sequence. | C# 8.0 |
Prepend | Prepends a value to the beginning of the sequence. | C# 8.0 |
ToHashSet | Creates a HashSet<T> from an IEnumerable<T>. | C# 8.0 |
Chunk | Splits the elements of a sequence into chunks of size at most chunkSize. | C# 10.0 |
IntersectBy | Produces the set intersection of two sequences according to a specified key selector. | C# 10.0 |
UnionBy | Produces the set union of two sequences according to a specified key selector. | C# 10.0 |
ExceptBy | Produces the set difference of two sequences according to a specified key selector. | C# 10.0 |
MaxBy | Returns the maximum value in a sequence according to a specified key selector. | C# 10.0 |
MinBy | Returns the minimum value in a sequence according to a specified key selector. | C# 10.0 |
Apart from the basic operations, LINQ also provides support for more advanced querying techniques such as joining, grouping, and working with complex data structures.
Join:
namespace LinqExamples;
class Program
{
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
}
public class Score
{
public int StudentId { get; set; }
public int ScoreValue { get; set; }
}
static void Main(string[] args)
{
var students = new List<Student>
{
new Student { StudentId = 1, Name = "John" },
new Student { StudentId = 2, Name = "Jane" }
};
var scores = new List<Score>
{
new Score { StudentId = 1, ScoreValue = 90 },
new Score { StudentId = 2, ScoreValue = 85 }
};
var studentScores = from student in students
join score in scores on student.StudentId equals score.StudentId
select new { student.Name, score.ScoreValue };
foreach (var studentScore in studentScores)
{
Console.WriteLine($"{studentScore.Name}: {studentScore.ScoreValue}");
}
}
}
namespace LinqExamples;
class Program
{
static void Main(string[] args)
{
var numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var groupedNumbers = from num in numbers
group num by num % 3 into numGroup
select new { Remainder = numGroup.Key, Numbers = numGroup.ToList() };
foreach (var group in groupedNumbers)
{
Console.WriteLine($"Numbers with remainder {group.Remainder} when divided by 3:");
foreach (var num in group.Numbers)
{
Console.WriteLine(num);
}
}
}
}
namespace LinqExamples;
class Program
{
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
static void Main(string[] args)
{
var students = new List<Student>
{
new Student { StudentId = 1, Name = "John", Age = 18 },
new Student { StudentId = 2, Name = "Jane", Age = 20 },
new Student { StudentId = 3, Name = "Jake", Age = 18 }
};
var studentGroups = from student in students
group student by student.Age into ageGroup
select new { Age = ageGroup.Key, Students = ageGroup.ToList() };
foreach (var group in studentGroups)
{
Console.WriteLine($"Students aged {group.Age}:");
foreach (var student in group.Students)
{
Console.WriteLine(student.Name);
}
}
}
}
Let's look at a practical example of using LINQ to query a collection of objects. Suppose we have a list of students and we want to filter, sort, and project this data.
Practical Example:
namespace LinqExamples;
class Program
{
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Grade { get; set; }
}
static void Main(string[] args)
{
var students = new List<Student>
{
new Student { StudentId = 1, Name = "John", Age = 18, Grade = 90 },
new Student { StudentId = 2, Name = "Jane", Age = 20, Grade = 85 },
new Student { StudentId = 3, Name = "Jake", Age = 18, Grade = 95 }
};
var topStudents = students
.Where(s => s.Grade > 85)
.OrderBy(s => s.Name)
.Select(s => new { s.Name, s.Grade });
foreach (var student in topStudents)
{
Console.WriteLine($"{student.Name}: {student.Grade}");
}
}
}
LINQ is a versatile and powerful tool in C# that simplifies data querying and manipulation. By integrating querying capabilities directly into the language, LINQ provides a consistent and readable approach to working with different types of data sources.