66. lambda expression
What is a Lambda Expression?
A lambda expression is a concise, anonymous
function that can be assigned to a delegate or used in LINQ queries. It
uses the => syntax.
Basic Example
Func<int, int> Square = x => x * x;
Console.WriteLine(Square(5)); // Output: 25
Here, x => x * x is a lambda expression that takes
x and returns x * x.
Benefits of Lambda Expressions
✅ Concise &
Readable
- Replaces
bulky delegate syntax with clean, one-liner expressions.
✅ Works Well
with LINQ
- Lambda
expressions are widely used in LINQ queries for filtering and
transforming data.
✅ No Need for
Separate Methods
- Useful
for small, one-off functions that don’t need a full method.
Comparison: Lambda vs Anonymous Method vs Normal Method
Feature |
Lambda Expression |
Anonymous Method |
Regular Method |
Syntax |
x => x * x |
delegate(int |
int Square(int |
Readability |
✅ Short & Clean |
❌ Verbose |
✅ Clear, but longer |
Performance |
⚠️ |
⚠️ |
✅ No extra allocation |
Reusability |
❌ One-time use only |
❌ One-time use only |
✅ Can be reused anywhere |
Limitations of Lambda Expressions
❌ Less Readable for
Complex Logic
- Simple
one-liners are great, but complex lambdas can become unreadable.
Func<int, int, int> Add = (x, y) => {
int sum = x + y;
return sum * 2;
};
This is harder to read than a simple method.
❌ Harder to Debug
- Since
lambda expressions don’t have a name, stack traces show anonymous
methods, making debugging difficult.
·
Best Use Cases for Lambda
Expressions
·
✔ Short, inline functions (e.g., x => x * x).
✔ LINQ Queries (myList.Where(x
=> x > 10)).
✔ Event Handlers (button.Click
+= (s, e) => Console.WriteLine(“Clicked!”);).
·
🚫 Avoid if the logic is complex—use a named function instead.
67. anonymous method
An anonymous method is a delegate without a name,
introduced in C# 2.0 (before lambda expressions existed).
Example
Func<int, int> Square = delegate(int x) { return x *
x; };
Console.WriteLine(Square(5)); // Output: 25
This was the old way of writing short, inline
functions before lambda expressions (x => x * x) were introduced.
Why Are Anonymous Methods Mostly Obsolete?
❌ More Verbose than
Lambdas
- Requires
delegate keyword and { }, making it longer than x => x * x.
❌ Same Limitations
as Lambdas
- Cannot
be reused. - Difficult
to debug (no function name).
✅ One Remaining
Use Case: Multi-line anonymous functions
- If
you need a long, inline function but don’t want a separate method,
you can still use them.
68. Action delegate
An Action<T> delegate represents a method
that does not return a value (void). It can take up to 16 parameters.
Basic Example
Action<string> Print = message =>
Console.WriteLine(message);
Print(“Hello, world!”); // Outputs: Hello, world!
Here, Action<string> is a delegate that accepts a
string but returns nothing (void).
Why Use Action Delegates?
✅ Encapsulates
Methods without Explicit Delegates
void ShowMessage(string message) =>
Console.WriteLine(message);
Action<string> Print = ShowMessage;
Print(“Hello!”);
// Works
✅ Avoids
Redundant Method Signatures
- You don’t
need to declare new delegate types—Action<T> is built-in.
Best Use Cases
for Action Delegates
·
✔ Event Handling (Action<object,
EventArgs> for UI events).
✔ Lambdas for Callbacks (Action<int>
callback = x => Console.WriteLine(x);).
✔ Encapsulating Void Functions Without
Declaring Extra Delegates.
·
🚫 Avoid if You Need a Return Value—use Func<T> instead.
69. Func delegate
A Func<T> delegate is a built-in generic
delegate that returns a value. It can take up to 16 input
parameters, making it highly flexible for different function signatures.
Unlike Action<T> (which returns void), Func<T> must
return a value. The last type parameter always represents the return
type, while any preceding ones represent input parameters.
Basic Example: Func<T> with One Parameter
Func<int, int> Square = x => x * x;
Console.WriteLine(Square(5)); // Output: 25
Here,
- Func<int,
int> means: takes an int, returns an int. - x
=> x * x is a lambda expression defining the function.
Func Delegate
with Multiple Parameters
·
Func<T1, T2, …, TResult> can have multiple input types and a return type.
Func<int, int, int> Multiply = (a, b) => a * b;
Console.WriteLine(Multiply(3, 4)); // Output: 12
Here,
- Func<int,
int, int> means: - First
int → first input parameter (a). - Second
int → second input parameter (b). - Last
int → return type.
Comparison: Func<T> vs Action<T>
Use Cases for Func<T>
✔ Math
operations (Func<double, double> for squaring, etc.)
✔ String manipulations
(Func<string, string> for formatting)
✔ Callbacks (Func<int, string>
for converting int to string)
✔ LINQ Queries (Func<T,
TResult> for filtering/selecting)
🚫 Avoid when you don’t
need a return value—use Action<T> instead.
Parting words
🔹
Func<T> is one of the most useful generic delegates in C#, making
function-based programming more concise and reusable. Use it whenever
you need a function that returns a value dynamically.
70. predicate
What is a Predicate?
A Predicate<T> is a specialized built-in delegate
that takes one parameter and always returns a bool. It is used for filtering,
validation, and conditional logic.
Basic Example
Predicate<int> IsEven = x => x % 2 == 0;
Console.WriteLine(IsEven(10)); // Output: True
Console.WriteLine(IsEven(7)); // Output: False
Here,
- Predicate<int>
means: - Takes
one int parameter. - Returns
a bool result (true or false).
Predicate
Delegate in LINQ
·
Predicates are commonly used in LINQ for
filtering collections.
List<int> numbers = new() { 1, 2, 3, 4, 5, 6 };
List<int> evenNumbers = numbers.FindAll(x => x % 2
== 0);
Console.WriteLine(string.Join(“, “,
evenNumbers)); // Output: 2, 4, 6
FindAll(Predicate<T>) filters the list using a predicate
function (x => x % 2 == 0).
Predicate vs Func<T, bool>
Both work identically, but Predicate<T> makes
intent clearer.
Best Use Cases for Predicates
✔ Filtering collections
(FindAll, Where, etc.)
✔ Input validation (Predicate<string> for
checking empty strings)
✔ Conditional event triggers (Predicate<int>
for threshold checks)
🚫 Avoid if you need
more than one parameter—use Func<T1, T2, bool> instead.
Predicate<T> is a specialized delegate for
bool-returning functions, making it ideal for filtering and validation
logic. Use it when clarity matters! ✅
71. generics
Generics in C# allow developers to define classes, interfaces, methods, and
delegates with type parameters instead of specifying concrete types. This makes
the code more reusable and type-safe. Generics eliminate the need for explicit
type casting and prevent type mismatches at runtime, as type checking is done
at compile time.
For example, a generic class can store values of any type
without duplicating code for each data type:
public class Box<T>
{
public T Value {
get; set; }
public Box(T
value) { Value = value; }
}
Here, T is a placeholder that gets replaced with an actual
type at runtime, allowing Box<int>, Box<string>, or Box<DateTime>
to work seamlessly.
Generics are extensively used in .NET collections such as List<T>,
Dictionary<TKey, TValue>, and Queue<T>, which provide better
performance and safety than non-generic collections.
Additionally, generics improve performance by reducing
boxing and unboxing operations when working with value types. Without generics,
storing an int in a non-generic ArrayList requires boxing it into an object,
which has performance overhead. Generics avoid this by preserving the original
type.
Another common use of generics is in constraint-based
programming, where generic methods are restricted to certain types using where
constraints. This ensures that only valid types are used, making the code more
predictable and secure.
72. type parameter
A type parameter is a placeholder type used in a generic
class, method, delegate, or interface. It allows developers to create flexible
and reusable code without specifying a concrete type upfront. Type parameters
are declared using angle brackets (<T>), where T represents the type to
be determined at runtime.
For example, a generic method that swaps two values can be
written as:
public void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
Here, T acts as a placeholder, allowing Swap<int>, Swap<double>,
or Swap<string> to work without separate method definitions.
Multiple type parameters can be used for more complex
generic structures:
public class Pair<T, U>
{
public T First {
get; set; }
public U Second {
get; set; }
public Pair(T
first, U second) { First = first; Second = second; }
}
In this case, Pair<int, string> can store an int and a
string, while Pair<double, bool> can store different types.
Type parameters improve type safety and reduce the need for
redundant code. Unlike using object, which requires type casting, generics
preserve the exact type, preventing runtime errors.