Generics and LINQ
Generics & LINQ
Introduction
In this lesson, you'll learn about generics and linq in C#. Coming from TypeScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In TypeScript, you're familiar with generics & linq.
C# has its own approach to generics & linq, which we'll explore step by step.
The C# Way
Let's see how C# handles this concept. Here's a typical example:
// C# generics — nearly identical syntax
T? First<T>(IEnumerable<T> source) => source.FirstOrDefault();
// LINQ — functional pipeline (like TS array methods)
var result = new[] { 1, 2, 3, 4, 5 }
.Where(n => n % 2 == 0)
.Select(n => n * 10)
.Sum();
// LINQ query syntax (SQL-like alternative)
var evens = from n in numbers
where n % 2 == 0
select n * 10;
// Generic interface with constraint
interface IRepository<T> where T : IHasId {
T? FindById(int id);
}Comparing to TypeScript
Here's how you might have written similar code in TypeScript:
// TypeScript generics
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
// Array methods as functional pipeline
const result = [1, 2, 3, 4, 5]
.filter(n => n % 2 === 0)
.map(n => n * 10)
.reduce((sum, n) => sum + n, 0);
// Generic interface with constraint
interface Repository<T extends { id: number }> {
findById(id: number): T | undefined;
}You may be used to different syntax or behavior.
C# generics use where T : Constraint instead of TypeScript's T extends Constraint
You may be used to different syntax or behavior.
LINQ .Where() = .filter(), .Select() = .map(), .Aggregate() = .reduce()
You may be used to different syntax or behavior.
LINQ has both method syntax and SQL-like query syntax
You may be used to different syntax or behavior.
C# generics support covariance (out T) and contravariance (in T) keywords
Step-by-Step Breakdown
1. T extends vs where T :
TypeScript uses <T extends Constraint>. C# uses <T> then a separate where T : Constraint clause at the end of the signature.
function sort<T extends Comparable<T>>(items: T[]): T[] {}IList<T> Sort<T>(IList<T> items) where T : IComparable<T> {}2. LINQ = Array Methods
LINQ (Language Integrated Query) provides the same functional pipeline as TypeScript array methods. Where/Select/Aggregate map to filter/map/reduce.
users.filter(u => u.age > 18).map(u => u.name)users.Where(u => u.Age > 18).Select(u => u.Name)3. Covariance and Contravariance
C# generic interfaces support explicit covariance (out T — read-only) and contravariance (in T — write-only). TypeScript infers variance automatically.
// TypeScript infers variance automatically
type ReadOnly<T> = { readonly value: T };// C# explicit covariance:
interface IProducer<out T> { T Produce(); }
// IProducer<Dog> is assignable to IProducer<Animal>4. IEnumerable<T> — The Universal Sequence
C# IEnumerable<T> is the equivalent of TypeScript's Iterable<T>. All LINQ methods work on any IEnumerable<T>, including arrays, lists, and custom sequences.
function process<T>(items: Iterable<T>): void {}void Process<T>(IEnumerable<T> items) {}
// Works with: arrays, List<T>, HashSet<T>, query resultsCommon Mistakes
When coming from TypeScript, developers often make these mistakes:
- C# generics use where T : Constraint instead of TypeScript's T extends Constraint
- LINQ .Where() = .filter(), .Select() = .map(), .Aggregate() = .reduce()
- LINQ has both method syntax and SQL-like query syntax
Key Takeaways
- C# constraint syntax: where T : IConstraint instead of T extends Constraint
- LINQ methods mirror TS array methods: Where/Select/Aggregate = filter/map/reduce
- C# supports explicit covariance (out T) and contravariance (in T)
- IEnumerable<T> is the universal sequence type — all LINQ methods operate on it