1. LINQ (Language Integrated Query)
LINQ is a powerful feature in C# that provides a query syntax similar to SQL for querying various data sources such as in-memory collections, databases, XML, etc. It can be used with collections like arrays, lists, and databases like Entity Framework.
Example: LINQ with In-Memory Collections
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// LINQ query to find even numbers
var evenNumbers = from num in numbers
where num % 2 == 0
select num;
foreach (var number in evenNumbers)
{
Console.WriteLine(number); // Output: 2, 4, 6, 8, 10
}
}
}
In this example, LINQ is used to filter even numbers from a list.
2. Lambda Expressions
Lambda expressions are concise, inline functions used primarily with LINQ queries and delegates. They are a shorthand syntax for anonymous methods.
Example: Lambda Expression in LINQ
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Lambda expression for filtering even numbers
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var number in evenNumbers)
{
Console.WriteLine(number); // Output: 2, 4, 6, 8, 10
}
}
}
Here, n => n % 2 == 0
is a lambda expression used to filter even numbers.
3. Expression Trees
Expression trees represent code in a tree-like data structure, where each node is an expression (e.g., method call, binary operation). Expression trees are useful in scenarios like LINQ to SQL, where queries need to be translated to another language (e.g., SQL).
Example: Expression Trees
using System;
using System.Linq.Expressions;
class Program
{
static void Main()
{
// Define an expression tree for a lambda expression
Expression<Func<int, bool>> expr = num => num > 5;
// Decompose and inspect the expression tree
Console.WriteLine($"Expression: {expr}");
Console.WriteLine($"Body: {expr.Body}");
Console.WriteLine($"Parameters: {string.Join(", ", expr.Parameters)}");
}
}
The output will show the structure of the expression tree, which can later be compiled or manipulated programmatically.
4. Extension Methods
Extension methods allow you to add methods to existing types without modifying the original type or using inheritance. They are defined as static methods in static classes but behave like instance methods.
Example: Extension Method
public static class StringExtensions
{
// Extension method for the string type
public static bool IsUpperCase(this string str)
{
return str == str.ToUpper();
}
}
class Program
{
static void Main()
{
string myString = "HELLO";
// Using the extension method
bool isUpper = myString.IsUpperCase();
Console.WriteLine(isUpper); // Output: True
}
}
The IsUpperCase
method extends the string
class and can be used like an instance method.
5. Anonymous Types
Anonymous types allow you to create objects without explicitly defining a class. They are useful for holding a set of properties in a concise manner, often in LINQ queries.
Example: Anonymous Types
class Program
{
static void Main()
{
var person = new { Name = "John", Age = 30 };
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}"); // Output: Name: John, Age: 30
}
}
Anonymous types are often used to quickly encapsulate data in LINQ queries or when working with data transformations.
6. Automatic Properties
Automatic properties simplify the declaration of properties by allowing the compiler to generate the backing field. You just need to declare the property, and the getter and setter are automatically handled.
Example: Automatic Properties
public class Person
{
// Automatic properties
public string Name { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main()
{
Person person = new Person { Name = "Alice", Age = 25 };
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}"); // Output: Name: Alice, Age: 25
}
}
Automatic properties reduce boilerplate code and simplify class declarations.
7. Object and Collection Initializers
Object initializers allow you to set properties during object creation, and collection initializers allow you to add elements to a collection when it's created. This can all be done in a single expression.
Example: Object and Collection Initializers
class Program
{
static void Main()
{
// Object initializer
var person = new Person { Name = "Bob", Age = 35 };
// Collection initializer
var numbers = new List<int> { 1, 2, 3, 4, 5 };
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}"); // Output: Name: Bob, Age: 35
Console.WriteLine(string.Join(", ", numbers)); // Output: 1, 2, 3, 4, 5
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
These initializers make the code more concise and readable.
8. Query Expressions
Query expressions provide a declarative, SQL-like syntax for LINQ queries. They are often used for complex queries because they are more readable and intuitive.
Example: Query Expression
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 30 },
new Person { Name = "Bob", Age = 25 },
new Person { Name = "Charlie", Age = 35 }
};
// Query expression syntax (similar to SQL)
var query = from p in people
where p.Age > 25
select p;
foreach (var person in query)
{
Console.WriteLine($"{person.Name} is {person.Age} years old.");
}
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
In this example, the query expression syntax (from
, where
, select
) filters the list of people based on their age.
Summary of Key Points
- LINQ provides SQL-like query capabilities for querying in-memory collections, databases, XML, and more.
- Lambda expressions offer a concise way to define inline methods, often used in LINQ queries.
- Expression trees represent code as data structures, useful for scenarios like LINQ to SQL, where code needs to be translated.
- Extension methods allow you to add methods to existing types without modifying them or using inheritance.
- Anonymous types enable quick object creation without explicit class definitions, useful for temporary data containers.
- Automatic properties streamline property declarations, letting the compiler handle the underlying storage.
- Object and collection initializers provide a concise way to initialize objects and collections during creation.
- Query expressions offer a declarative, SQL-like syntax for LINQ queries, making them more readable and powerful for complex data queries.