Skip to main content

Project Setup Guide

Choosing Your Showcase Project

Select the project that best aligns with your interests and target role:

High-Throughput Logger

Best for: Systems programming, infrastructure roles Complexity: Medium Key Concepts: Async I/O, buffering, Span<T>, producer-consumer

In-Memory Event Store

Best for: Domain-driven design, event sourcing roles Complexity: Medium-High Key Concepts: Event sourcing, CQRS, async streams, concurrency

Task Scheduler

Best for: Backend services, distributed systems roles Complexity: High Key Concepts: Advanced threading, async coordination, priority queues


Project Structure

Create a well-organized solution structure:

mkdir csharp-mastery-showcase
cd csharp-mastery-showcase

# Create solution
dotnet new sln -n MasteryShowcase

# Create projects
dotnet new classlib -n MasteryShowcase.Core
dotnet new classlib -n MasteryShowcase.Infrastructure
dotnet new console -n MasteryShowcase.Examples
dotnet new xunit -n MasteryShowcase.Tests
dotnet new console -n MasteryShowcase.Benchmarks

# Add to solution
dotnet sln add **/*.csproj

# Add BenchmarkDotNet
cd MasteryShowcase.Benchmarks
dotnet add package BenchmarkDotNet
cd ..

Target Framework

Use .NET 8 or .NET 9 (preview) to access C# 12-14 features:

<!-- Directory.Build.props in solution root -->
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>preview</LangVersion> <!-- For C# 13-14 features -->
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

Example: High-Throughput Logger Setup

Core Domain (MasteryShowcase.Core)

// LogLevel.cs - using C# 12 features
namespace MasteryShowcase.Core;

public enum LogLevel
{
Trace,
Debug,
Information,
Warning,
Error,
Critical
}

// LogEntry.cs - using record struct (C# 10+)
public readonly record struct LogEntry(
LogLevel Level,
string Message,
DateTime Timestamp,
string? Category = null)
{
public override string ToString() =>
$"[{Timestamp:yyyy-MM-dd HH:mm:ss}] [{Level}] {Category}: {Message}";
}

// ILogTarget.cs - using C# 14 features if available
public interface ILogTarget : IAsyncDisposable
{
ValueTask WriteAsync(ReadOnlyMemory<LogEntry> entries, CancellationToken ct = default);
ValueTask FlushAsync(CancellationToken ct = default);
}

Using Primary Constructors

// Logger.cs - demonstrating C# 12 primary constructor
namespace MasteryShowcase.Core;

public class Logger(ILogTarget target, LogLevel minimumLevel) : IAsyncDisposable
{
private readonly Channel<LogEntry> _channel = Channel.CreateUnbounded<LogEntry>();
private readonly Task _processingTask = Task.CompletedTask;

// Properties from primary constructor parameters
public LogLevel MinimumLevel { get; } = minimumLevel;

public void Log(LogLevel level, string message, string? category = null)
{
if (level < MinimumLevel)
return;

var entry = new LogEntry(level, message, DateTime.UtcNow, category);
_channel.Writer.TryWrite(entry);
}

private async Task ProcessLogsAsync(CancellationToken ct)
{
// Use target from primary constructor
await foreach (var entry in _channel.Reader.ReadAllAsync(ct))
{
await target.WriteAsync(new[] { entry }, ct);
}
}

public async ValueTask DisposeAsync()
{
_channel.Writer.Complete();
await _processingTask;
await target.DisposeAsync();
}
}

Using Collection Expressions

// BatchingLogTarget.cs - demonstrating collection expressions
namespace MasteryShowcase.Infrastructure;

public class BatchingLogTarget(ILogTarget innerTarget, int batchSize) : ILogTarget
{
private readonly List<LogEntry> _buffer = [];
private readonly Lock _lock = new(); // C# 13 Lock type

public async ValueTask WriteAsync(ReadOnlyMemory<LogEntry> entries, CancellationToken ct = default)
{
List<LogEntry> toFlush = [];

using (_lock.EnterScope())
{
// Collection expression with spread operator
_buffer.AddRange(entries.ToArray());

if (_buffer.Count >= batchSize)
{
toFlush = [.._buffer]; // Spread operator
_buffer.Clear();
}
}

if (toFlush.Count > 0)
{
await innerTarget.WriteAsync(toFlush.ToArray(), ct);
}
}

public async ValueTask FlushAsync(CancellationToken ct = default)
{
List<LogEntry> toFlush;
using (_lock.EnterScope())
{
toFlush = [.._buffer];
_buffer.Clear();
}

if (toFlush.Count > 0)
{
await innerTarget.WriteAsync(toFlush.ToArray(), ct);
}
}

public async ValueTask DisposeAsync()
{
await FlushAsync();
await innerTarget.DisposeAsync();
}
}

Using Span<T> for Performance

// SpanFormatter.cs - demonstrating Span\<T\> usage
namespace MasteryShowcase.Core;

public static class LogFormatter
{
// params with Span - C# 13 feature
public static int Format(Span\<char\> destination, in LogEntry entry)
{
int position = 0;

// Format timestamp
if (!entry.Timestamp.TryFormat(destination[position..], out int written, "yyyy-MM-dd HH:mm:ss"))
return 0;
position += written;

// Add level
ReadOnlySpan\<char\> level = entry.Level.ToString();
if (position + level.Length + 4 > destination.Length)
return 0;

destination[position++] = ' ';
destination[position++] = '[';
level.CopyTo(destination[position..]);
position += level.Length;
destination[position++] = ']';
destination[position++] = ' ';

// Add message
ReadOnlySpan\<char\> message = entry.Message;
if (position + message.Length > destination.Length)
message = message[..(destination.Length - position)];

message.CopyTo(destination[position..]);
position += message.Length;

return position;
}
}

Initial Features Checklist

For Phase 4 Days 1-2, implement:

Core Features

  • Define domain models (record structs)
  • Create main abstractions with primary constructors
  • Implement basic functionality
  • Add XML documentation

C# 12-14 Feature Usage

  • Primary constructors for DI
  • Collection expressions where applicable
  • record struct for value objects
  • New Lock type (C# 13) if locking needed

Project Setup

  • Solution structure created
  • Projects reference each other correctly
  • Target framework configured (.NET 8+)
  • LangVersion set to preview
  • Basic README.md started

README.md Template

# [Your Project Name]

## Overview

Brief description of what the project does.

## C# 12-14 Features Demonstrated

### Primary Constructors (C# 12)

- Used in Logger class for DI
- Shows parameter scope and property creation

```csharp
public class Logger(ILogTarget target, LogLevel minimumLevel)
{
public LogLevel MinimumLevel { get; } = minimumLevel;
}
```

Collection Expressions (C# 12)

  • Used for combining log batches
var batch = [..buffer1, ..buffer2];

New Lock Type (C# 13)

  • Type-safe locking in BatchingLogTarget

[Continue documenting each feature...]

Architecture

[Explain your design]

Performance

[Benchmarks and results]

Usage

// Example usage
var logger = new Logger(...);
logger.Log(LogLevel.Information, "Hello, World!");

Building and Running

dotnet build
dotnet test
dotnet run --project MasteryShowcase.Examples

## Next Steps

After basic setup:
1. Implement core functionality
2. Add unit tests
3. Move to Day 3-4: Advanced features
4. Add threading/synchronization
5. Implement async patterns
6. Optimize with Span\<T\>/Memory\<T\>
7. Add benchmarks

---

**Continue to:** [Advanced Features](./02-advanced-features.md) after completing basic setup.