C sharp - Functional Programming Concepts in C#
C# is primarily an object-oriented language, but it strongly supports functional programming (FP) principles.
Functional programming focuses on:
-
Pure functions
-
Immutability
-
Higher-order functions
-
Declarative style
-
Avoiding side effects
Modern C# (especially C# 7–12) includes many FP features.
1. Pure Functions
A pure function:
-
Always returns the same output for same input
-
Has no side effects
-
Does not modify external state
Example:
int Add(int a, int b)
{
return a + b;
}
Not pure:
int total = 0;
int Add(int a)
{
total += a; // modifies external state
return total;
}
Pure functions are:
-
Predictable
-
Easy to test
-
Thread-safe
2. Immutability
In FP, data should not change after creation.
Mutable object:
class Person
{
public string Name { get; set; }
}
Immutable object:
record Person(string Name);
Records (introduced in C# 9) are designed for immutability.
Benefits:
-
Safer concurrency
-
No unexpected state changes
-
Easier debugging
3. Higher-Order Functions
A higher-order function:
-
Takes a function as parameter
-
Returns a function
Example:
Func<int, int> square = x => x * x;
int Execute(Func<int, int> operation, int value)
{
return operation(value);
}
Usage:
Console.WriteLine(Execute(square, 5));
This is core functional design.
4. Lambda Expressions
Lambda expressions allow inline functions:
(x, y) => x + y
Used heavily in:
-
LINQ
-
Delegates
-
Async programming
They make code more declarative.
5. LINQ (Declarative Programming)
LINQ is one of the strongest FP features in C#.
Instead of writing loops:
List<int> result = new List<int>();
foreach (var x in numbers)
{
if (x > 5)
result.Add(x);
}
Use LINQ:
var result = numbers.Where(x => x > 5).ToList();
This is:
-
Declarative
-
More readable
-
Functional style
6. First-Class Functions
Functions can be:
-
Assigned to variables
-
Passed as parameters
-
Returned from methods
Example:
Func<int, int, int> multiply = (a, b) => a * b;
C# treats functions as values.
7. Function Composition
Combine functions:
Func<int, int> add2 = x => x + 2;
Func<int, int> multiply3 = x => x * 3;
Func<int, int> composed = x => multiply3(add2(x));
Console.WriteLine(composed(5)); // (5+2)*3 = 21
This avoids intermediate variables.
8. Pattern Matching
Modern C# supports pattern matching:
static string Check(object obj) =>
obj switch
{
int i when i > 0 => "Positive Integer",
string s => "String",
_ => "Unknown"
};
Pattern matching improves:
-
Code clarity
-
Functional-style branching
9. Avoiding Null (Option-like Pattern)
Functional programming discourages nulls.
C# uses:
-
Nullable reference types
-
?.operator -
??operator
Example:
string name = user?.Name ?? "Unknown";
Some libraries implement:
-
Option
-
Maybe pattern
To eliminate null-related bugs.
10. Recursion Over Loops
Functional style often prefers recursion.
Example:
int Factorial(int n)
{
if (n <= 1) return 1;
return n * Factorial(n - 1);
}
Though C# doesn't optimize tail recursion fully, recursion is still supported.
11. No Shared State (Concurrency Advantage)
Immutability + pure functions:
-
Safe parallel execution
-
No race conditions
-
Easier async programming
This is why FP concepts are powerful in modern backend systems.
12. OOP vs Functional in C#
| OOP | Functional |
|---|---|
| Focus on objects | Focus on functions |
| Mutable state common | Prefer immutability |
| Behavior inside classes | Behavior passed as functions |
| Side effects common | Avoid side effects |
C# supports hybrid approach.
Real-World Usage
Functional concepts are used in:
-
LINQ queries
-
Async workflows
-
Data transformation pipelines
-
Reactive programming
-
Microservices