JS
C#

JavaScript to C#

10 lessons

Progress0%
1Variables & Types2Classes & OOP3Async/Await4Array Methods → LINQ5Exception Handling6Collections7Generics8Delegates and Events9Records and Pattern Matching10File I/O
All Mirror Courses
JS
C#
Generics
MirrorLesson 7 of 10
Lesson 7

Generics

Type parameters, constraints, and reusable generic types

Introduction

In this lesson, you'll learn about generics in C#. Coming from JavaScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
JS
From JavaScript:

In JavaScript, you're familiar with type parameters, constraints, and reusable generic types.

C#
In C#:

C# has its own approach to type parameters, constraints, and reusable generic types, which we'll explore step by step.

The C# Way

Let's see how C# handles this concept. Here's a typical example:

C#
C# Example
// Generic method
T Identity<T>(T value) => value;

// Generic class
public class Box<T>
{
    public T Value { get; }
    public Box(T value) => Value = value;
}

// Type constraints
T Max<T>(T a, T b) where T : IComparable<T>
    => a.CompareTo(b) > 0 ? a : b;

// Multiple constraints
void Register<T>(T entity)
    where T : class, IEntity, new()
{ /* T must be a ref type, implement IEntity, and have parameterless ctor */ }

// Generic interface
public interface IRepository<T> where T : class
{
    T GetById(int id);
    void Save(T entity);
}

// Usage
var box = new Box<string>("hello");
Console.WriteLine(box.Value);       // "hello"
Max(3, 7);                          // 7

Comparing to JavaScript

Here's how you might have written similar code in JavaScript:

JS
JavaScript (What you know)
// JS has no generics — everything is dynamic
function wrap(value) {
  return { value };
}

function first(arr) {
  return arr[0];
}

// No type safety
wrap("text");   // ok
wrap(42);       // ok
first([1,2,3]); // 1
Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

C# generics are reified — type info is preserved at runtime (unlike Java's erasure)

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

'where T : class' constrains T to reference types; 'where T : struct' to value types

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

'where T : new()' requires a parameterless constructor

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

Multiple constraints: where T : IFoo, IBar, new()

Mirror Card
JS
From JavaScript:

You may be used to different syntax or behavior.

C#
In C#:

Covariance/contravariance via out/in keywords on generic interfaces

Step-by-Step Breakdown

1. Generic Methods

Append <T> after the method name. Type inference usually means you don't need to specify T explicitly.

JS
JavaScript
function wrap(v) { return { value: v }; }
C#
C#
T Wrap<T>(T v) => v;
// Caller: Wrap("text") — T inferred as string

2. Generic Classes

Generic classes let you build type-safe containers. T is fixed at instantiation time.

JS
JavaScript
class Stack { push(v) {...} pop() {...} }
C#
C#
class Stack<T> {
    List<T> _items = new();
    public void Push(T item) => _items.Add(item);
    public T Pop() { var v = _items[^1]; _items.RemoveAt(_items.Count-1); return v; }
}

3. Type Constraints

Constraints let you call interface methods on T. Without constraints, only object members are available.

JS
JavaScript
function max(a, b) { return a > b ? a : b; }
C#
C#
T Max<T>(T a, T b) where T : IComparable<T>
    => a.CompareTo(b) >= 0 ? a : b;
Rule of Thumb
Add constraints only when needed — they limit callers but enable more operations on T.

4. Variance (out/in)

out T = covariant (can return T but not accept); in T = contravariant (can accept T but not return). Used on interfaces only.

JS
JavaScript
// No equivalent in JS
C#
C#
interface IProducer<out T> { T Produce(); }
interface IConsumer<in T> { void Consume(T item); }
Common Pitfall
Variance only applies to interface/delegate type parameters, not concrete class type parameters.

Common Mistakes

When coming from JavaScript, developers often make these mistakes:

  • C# generics are reified — type info is preserved at runtime (unlike Java's erasure)
  • 'where T : class' constrains T to reference types; 'where T : struct' to value types
  • 'where T : new()' requires a parameterless constructor
Common Pitfall
Don't assume C# works exactly like JavaScript. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Generic methods: T Method<T>(T arg); classes: class Foo<T>
  • Constraints (where) enable operations on T: IComparable, class, struct, new()
  • C# generics are reified — typeof(T) works at runtime, unlike Java's erasure
  • Use out/in on interface type parameters for covariance/contravariance
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your JavaScript code in C# to practice these concepts.
PreviousNext