C# Story

Chapter #4 – Operations

methods, delegates, lambdas, LINQ, extension methods

4.0 Prologue

This chapter covers the mechanisms C# provides for expressing computation: methods in all their forms, function-typed delegates, compact lambda expressions, and the powerful LINQ query model that ties them together.

4.1 Methods

A method belongs to a type. Its signature includes the return type, name, and parameter list. Parameters are passed by value by default; ref and out pass by reference; in passes a read-only reference. int Add(int a, int b) => a + b; // expression-bodied void Swap(ref int x, ref int y) { // ref parameters (x, y) = (y, x); } bool TryParse(string s, out int result) { // out parameter return int.TryParse(s, out result); } Optional parameters have defaults and must trail required ones. Named arguments let callers supply values in any order: Print(message: "hi", color: ConsoleColor.Green); params lets a method accept a variable number of arguments: int Sum(params int[] values) => values.Sum(); Local functions (C# 7+) are methods defined inside another method. They can capture outer variables, have full type inference, and are invisible outside their enclosing method. Overloading: multiple methods may share a name if their parameter lists differ in count or type. Return type alone does not distinguish overloads.

4.2 Operators

C# provides the usual arithmetic, relational, logical, and bitwise operators. Operator precedence follows mathematical convention; use parentheses when in doubt. Operator overloading: value types and classes may define custom behavior for operators such as +, -, ==, and comparison operators: readonly struct Vec2 { public double X { get; init; } public double Y { get; init; } public static Vec2 operator+(Vec2 a, Vec2 b) => new Vec2 { X = a.X + b.X, Y = a.Y + b.Y }; public static bool operator==(Vec2 a, Vec2 b) => a.X == b.X && a.Y == b.Y; public static bool operator!=(Vec2 a, Vec2 b) => !(a == b); } If you overload == you should also override Equals() and GetHashCode() to keep them consistent. Conversion operators (implicit / explicit) define how a type converts to or from another type.

4.3 Delegates

A delegate is a type-safe reference to a method (or group of methods). The BCL supplies two generic families that cover almost all use cases:
  • Action<T1, T2, …> — void return
  • Func<T1, T2, …, TResult> — non-void return
  • Predicate<T> — equivalent to Func<T, bool>
Func<int, int, int> add = (a, b) => a + b; Action<string> print = Console.WriteLine; int result = add(3, 4); // 7 print("hello"); // writes hello Delegates are multicast: use += to chain multiple methods onto the same delegate variable. Invoking the delegate calls all chained methods in order. Events wrap a delegate field and restrict outside callers to only += and -= operations — the owning class retains the right to raise the event: class Button { public event Action? Clicked; public void Click() => Clicked?.Invoke(); }

4.4 Lambdas and Anonymous Methods

A lambda expression is an inline anonymous function. It can be assigned to a compatible delegate type or passed directly as an argument: Func<int, bool> isEven = n => n % 2 == 0; // statement lambda (multiple lines) Func<int, int> factorial = n => { int r = 1; for (int i = 2; i <= n; i++) r *= i; return r; }; Lambdas capture variables from the enclosing scope by reference (a closure). Be careful when capturing loop variables — each iteration shares the same variable unless you copy it to a local. Static lambdas (C# 9+, prefix static) are forbidden from capturing instance state, helping avoid unintended allocations.

4.5 LINQ

Language Integrated Query (LINQ) provides a unified model for querying any sequence that implements IEnumerable<T>, including arrays, lists, XML trees, database result sets, and more. Two equivalent syntaxes — query syntax (SQL-like) and method syntax (fluent extension methods): int[] nums = { 5, 3, 8, 1, 9, 2, 6 }; // query syntax var q1 = from n in nums where n > 4 orderby n descending select n * n; // method syntax (identical result) var q2 = nums.Where(n => n > 4) .OrderByDescending(n => n) .Select(n => n * n); Key LINQ operators:
  • Where — filter by predicate
  • Select — transform each element
  • SelectMany — flatten nested sequences
  • OrderBy / OrderByDescending — sort
  • GroupBy — partition into keyed groups
  • Join / GroupJoin — correlate two sequences
  • Distinct / Union / Intersect / Except — set operations
  • First / Last / Single / ElementAt — element access
  • Any / All / Count / Sum / Min / Max / Average — aggregation
  • ToList / ToArray / ToDictionary — materialize
LINQ queries are lazy: they do not execute until the sequence is enumerated (e.g., in a foreach, or when calling ToList()). This enables efficient pipelining and deferred evaluation.

4.6 Extension Methods

An extension method is a static method defined in a static class whose first parameter is prefixed with this. It is called as if it were an instance method on the target type: public static class StringExtensions { public static bool IsPalindrome(this string s) { int n = s.Length; for (int i = 0; i < n / 2; i++) if (s[i] != s[n - 1 - i]) return false; return true; } } // usage bool ok = "racecar".IsPalindrome(); // true All LINQ operators are extension methods on IEnumerable<T> defined in System.Linq. Extension methods can be defined for interfaces, sealed classes, or any type you cannot modify.

4.7 Iterators

A method that contains yield return becomes an iterator. It returns an IEnumerable<T> and produces values lazily — each call to the enumerator's MoveNext() resumes execution at the next yield return: IEnumerable<int> Fibonacci() { int a = 0, b = 1; while (true) { yield return a; (a, b) = (b, a + b); } } foreach (int f in Fibonacci().Take(10)) Console.Write(f + " "); // 0 1 1 2 3 5 8 13 21 34 yield break ends the iteration early. Iterators compose naturally with LINQ operators.

4.8 Epilogue

This chapter covered C# computation: methods in all their forms, operators, delegates and events, lambdas, LINQ, extension methods, and iterators. The next chapter explores how data and operations are packaged into classes.

4.9 References

LINQ — Microsoft docs
Delegates — Microsoft docs
Lambda expressions
Extension methods