---
title: Option
---

The `Option<TValue>` monad represents an optional value that may or may not be present. It provides a type-safe alternative to null references, making the optionality explicit in the type system.

## Purpose

`Option<T>` eliminates null reference exceptions by forcing explicit handling of the absence of a value. Instead of returning `null` and hoping consumers remember to check, `Option<T>` makes the optionality part of the method signature.

## Usage

### Producer Perspective

Return `Option<T>` from methods that may not always have a value:

```csharp
public class UserRepository
{
    public Option<User> FindById(UserId id)
    {
        var user = _database.Users.FirstOrDefault(u => u.Id == id);
        
        // Implicit conversion from TValue to Option<TValue>
        if (user is not null)
            return user;
        
        // Return None when value is absent
        return Option<User>.None();
    }
    
    public Option<string> GetEmailAddress(UserId userId)
    {
        var user = FindById(userId);
        if (!user.HasValue)
            return Option<string>.None();
        
        if (user.TryGetValue(out var userValue) && !string.IsNullOrEmpty(userValue.Email))
            return userValue.Email;
        
        return Option<string>.None();
    }
}
```

### Consumer Perspective

Explicitly handle the presence or absence of values:

```csharp
public class UserService
{
    readonly UserRepository _repository;
    
    public void ProcessUser(UserId id)
    {
        var userOption = _repository.FindById(id);
        
        // Check if value is present
        if (userOption.HasValue)
        {
            // TryGetValue provides safe access
            if (userOption.TryGetValue(out var user))
            {
                Console.WriteLine($"Processing user: {user.Name}");
            }
        }
        else
        {
            Console.WriteLine("User not found");
        }
    }
    
    public void DisplayUserEmail(UserId id)
    {
        var emailOption = _repository.GetEmailAddress(id);
        
        // TryGetValue approach
        if (emailOption.TryGetValue(out var email))
        {
            Console.WriteLine($"Email: {email}");
        }
        else
        {
            Console.WriteLine("No email available");
        }
    }
    
    public string GetUserDisplayName(UserId id)
    {
        var userOption = _repository.FindById(id);
        
        // Using Match() for functional composition
        return userOption.Match(
            value => value.Name,
            none => "Unknown User");
    }
    
    public void ProcessUserWithMatch(UserId id)
    {
        var userOption = _repository.FindById(id);
        
        // Switch() for side effects (Match returns a value; Switch is for actions)
        userOption.Switch(
            value => Console.WriteLine($"Processing user: {value.Name}"),
            none => Console.WriteLine("User not found"));
    }
    
    public void DisplayUserWithPatternMatching(UserId id)
    {
        var userOption = _repository.FindById(id);
        
        // Pattern matching with switch expression
        var message = userOption switch
        {
            { IsT0: true } => $"Found user: {userOption.AsT0.Name}",
            _ => "User not found"
        };
        
        Console.WriteLine(message);
    }
}
```

## Key Points

- Use `Option<T>` instead of returning `null` for optional values
- The `HasValue` property indicates if a value is present
- `TryGetValue` provides safe value extraction similar to Dictionary's TryGetValue pattern
- Use `Match()` for functional composition and transforming values
- Pattern matching with `switch` expressions enables concise conditional logic
- Implicit conversion from `TValue` to `Option<TValue>` simplifies creation
- Makes API contracts explicit - callers know they must handle absence
