Ticker

6/recent/ticker-posts

Header Ads Widget

The Essential C#/.NET Handbook # 66 - 72

 

The Essential C#/.NET Handbook # 66 - 72 - Encyclopedia of C-Sharp Notions

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 x) { return x * x; }

int Square(int x) { return x * x; }

Readability

Short & Clean

Verbose

Clear, but longer

Performance

⚠️ Allocates Delegate

⚠️ Allocates Delegate

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.