C# Programming Labs

Master high-performance patterns: Span/Memory for zero-allocation code, source generators, and async channels.

Memory, Generators & Channels - Module 10

Write high-performance, modern C# with cutting-edge features.

Lab 28: Span<T> and Memory<T>
Expert
Coding Challenge
Your Task: Use Span<T> and Memory<T> for high-performance, zero-allocation operations on contiguous memory.

Detailed Requirements:
1. Create Span from array: int[] numbers = { 1, 2, 3, 4, 5 }; Span<int> span = numbers.AsSpan(); // or: Span<int> span = new Span<int>(numbers); 2. Slice without allocation: Span<int> slice = span.Slice(1, 3); // [2, 3, 4] // Modifications affect original array! slice[0] = 99; // numbers[1] is now 99 3. Use ReadOnlySpan for strings: ReadOnlySpan<char> text = "Hello, World!".AsSpan(); ReadOnlySpan<char> hello = text.Slice(0, 5); // "Hello" 4. Parse without allocation: ReadOnlySpan<char> numStr = "12345".AsSpan(); int parsed = int.Parse(numStr); 5. Use Memory<T> for async scenarios: Memory<int> mem = numbers.AsMemory(); // Memory can be stored in fields, passed to async methods

💡 Pro Tips:
• Span is stack-only (ref struct) - can't be stored in fields or used in async
• Memory<T> is heap-safe and can be used anywhere
• Use .Span property to get Span from Memory
• Slicing creates a view, not a copy - zero allocations!

Expected Output: Original: 1, 2, 3, 4, 5 Slice [1..4]: 2, 3, 4 After modification: 1, 99, 3, 4, 5 String slice: Hello Parsed number: 12345

Requirements Checklist

Create Span from array with AsSpan()
Use Slice() to create a view
Modify slice and show effect on original
Use ReadOnlySpan for string slicing
Parse number from ReadOnlySpan
Demonstrate Memory<T> usage
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Create Span: Span<int> s = array.AsSpan();
• Slice: span.Slice(start, length) or span[1..4]
• String: ReadOnlySpan<char> = "text".AsSpan();
• Memory: Memory<int> m = array.AsMemory();
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 29: Source Generators Concepts
Expert
Coding Challenge
Your Task: Understand source generators by simulating generated code patterns. Source generators create code at compile time.

Detailed Requirements:
1. Understand the pattern - attributes mark targets: [AutoNotify] // Attribute marks class for generation public partial class Person { private string _name; private int _age; } 2. Generated code adds functionality: // Generated partial class public partial class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } } 3. Simulate a ToString generator: [GenerateToString] public partial class Product { public int Id { get; set; } public string Name { get; set; } } // Generated: public override string ToString() => $"Product {{ Id = {Id}, Name = {Name} }}"; 4. Use partial methods: partial class MyClass { partial void OnCreated(); // Declaration } partial class MyClass { partial void OnCreated() { Console.WriteLine("Created!"); } // Implementation }

💡 Pro Tips:
• Source generators run at compile time, not runtime
• They analyze syntax/semantic models to generate code
• Must use partial classes to combine generated + handwritten code
• Common uses: serializers, DI registration, boilerplate elimination

Expected Output: Person: Alice, Age: 30 PropertyChanged fired for: Name Product: Product { Id = 1, Name = Widget } OnCreated was called!

Requirements Checklist

Create custom attribute class
Use partial class pattern
Implement INotifyPropertyChanged pattern
Create simulated generated ToString()
Use partial methods
Demonstrate the generated-like behavior
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Attribute: [AttributeUsage(AttributeTargets.Class)]
• Partial: public partial class Name { }
• INotifyPropertyChanged: PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
• Partial method: partial void MethodName();
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 30: System.Threading.Channels
Expert
Coding Challenge
Your Task: Use Channel<T> for high-performance async producer-consumer patterns.

Detailed Requirements:
1. Create a bounded channel: var channel = Channel.CreateBounded<int>(new BoundedChannelOptions(10) { FullMode = BoundedChannelFullMode.Wait }); 2. Create an unbounded channel: var channel = Channel.CreateUnbounded<string>(); 3. Write to channel (producer): var writer = channel.Writer; await writer.WriteAsync(42); writer.Complete(); // Signal no more items 4. Read from channel (consumer): var reader = channel.Reader; await foreach (var item in reader.ReadAllAsync()) { Console.WriteLine(item); } 5. Multiple producers/consumers: // Start producer task var producer = Task.Run(async () => { ... }); // Start consumer task var consumer = Task.Run(async () => { ... }); await Task.WhenAll(producer, consumer);

💡 Pro Tips:
• Channels are thread-safe by design
• Bounded channels apply backpressure (producers wait when full)
• Always call Complete() when done writing
• ReadAllAsync() returns IAsyncEnumerable<T>

Expected Output: Producer: Writing 0 Producer: Writing 1 Consumer: Read 0 Consumer: Read 1 Producer: Writing 2 Consumer: Read 2 All done!

Requirements Checklist

Create bounded or unbounded channel
Use ChannelWriter to produce items
Use ChannelReader to consume items
Use ReadAllAsync with await foreach
Call Complete() when done writing
Run producer and consumer concurrently
Output
// Click "Run Code" to compile and execute
Hints & Tips
• Bounded: Channel.CreateBounded<T>(capacity)
• Unbounded: Channel.CreateUnbounded<T>()
• Write: await writer.WriteAsync(item);
• Read: await foreach (var x in reader.ReadAllAsync())
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Congratulations!

You've completed all 30 C# Programming Labs! You now have comprehensive skills in modern C# development, from fundamentals to advanced patterns.