C#

Lesson 01

Nullable reference boundaries

Use nullable reference types to show when a lookup can miss, instead of returning a non-null type that may hide null.

Good Code

src/ReviewService.cs
public sealed class ReviewService
{
    private readonly ReviewRepository reviews;

    public ReviewService(ReviewRepository reviews)
    {
        this.reviews = reviews;
    }

    public async Task<ReviewDto?> FindAsync(
        Guid id,
        CancellationToken cancellationToken)
    {
        // Nullable return type tells callers the review may be missing.
        Review? review = await reviews.FindAsync(id, cancellationToken);
        return review is null ? null : ReviewDto.From(review);
    }
}

Bad Code

ReviewService.cs
public sealed class ReviewService
{
    private readonly ReviewRepository reviews;

    public ReviewDto Find(string id)
    {
        // Non-null return type hides the missing-review path.
        var review = reviews.Find(id);
        return new ReviewDto(review.Title, review.Score);
    }
}

Review Notes

What to review

Good Code

The good version returns ReviewDto?, making the miss path part of the contract. The caller must decide whether to return 404, show empty state, or continue.

Bad Code

The bad version promises a non-null DTO but depends on a repository result that may be null. The failure appears later as a NullReferenceException.

Takeaways

  • A C# method should make absence visible in its return type so callers choose the missing-data response at the boundary.