diff --git a/docs/src/content/docs/guide/getting-started-csharp.mdx b/docs/src/content/docs/guide/getting-started-csharp.mdx
index 41b413e1..55d51043 100644
--- a/docs/src/content/docs/guide/getting-started-csharp.mdx
+++ b/docs/src/content/docs/guide/getting-started-csharp.mdx
@@ -2,86 +2,204 @@
title: Getting started (C#)
---
-import {Tabs, TabItem, LinkCard} from "@astrojs/starlight/components";
+import { Tabs, TabItem, LinkCard } from "@astrojs/starlight/components";
-## Supported Runtimes
+# Getting Started with Bebop in C#
+
+Bebop is a high-performance serialization framework designed for efficient data transfer. This guide will walk you through setting up Bebop in your C# project, creating schemas, and using the generated code.
+
+## Supported Runtimes
- .NET Framework 4.7.2
- .NET Framework 4.8
- .NET Core 3.1
- .NET 5+
-## Install the Runtime
-To install the Bebop .NET runtime you can use the following commands:
+## Installation
-```
-dotnet add package bebop
-```
+First, let's install the necessary packages:
-| Package | NuGet Stable | Downloads |
-| :--------------------------------------------- | :------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------- |
+| Package | NuGet Stable | Downloads |
+| :------ | :----------- | :-------- |
| [bebop](https://www.nuget.org/packages/bebop/) | [![bebop](https://img.shields.io/nuget/v/bebop.svg)](https://www.nuget.org/packages/bebop/) | [![bebop](https://img.shields.io/nuget/dt/bebop.svg)](https://www.nuget.org/packages/bebop/) |
+| [bebop-tools](https://www.nuget.org/packages/bebop-tools/) | [![bebop-tools](https://img.shields.io/nuget/v/bebop-tools.svg)](https://www.nuget.org/packages/bebop-tools/) | [![bebop-tools](https://img.shields.io/nuget/dt/bebop-tools.svg)](https://www.nuget.org/packages/bebop-tools/) |
-## Install the Compiler Tools
-To install the Bebop Compiler tools you can use the following command:
+### Install Bebop Runtime
+```bash
+dotnet add package bebop
```
+
+### Install Bebop Compiler Tools
+
+```bash
dotnet add package bebop-tools
```
-| Package | NuGet Stable | Downloads |
-| :--------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------- |
-| [bebop-tools](https://www.nuget.org/packages/bebop-tools/) | [![bebop-tools](https://img.shields.io/nuget/v/bebop-tools.svg)](https://www.nuget.org/packages/bebop-tools/) | [![bebop-tools](https://img.shields.io/nuget/dt/bebop-tools.svg)](https://www.nuget.org/packages/bebop-tools/) |
-## Configuring the Compiler Tools
+## Project Configuration
-Inside of your project file add a new `ItemGroup`
+To configure Bebop in your project, add the following `ItemGroup` to your `.csproj` file:
```xml
-
+
```
-When the `` item group is present in your project all schemas that match the provided `Include` path are compiled in accordance with the output parameters defined whenever you build your project (meaning that schema changes reflect immediately in your project.)
+This configuration tells Bebop to:
+- Include all `.bop` files in your project
+- Generate C# code
+- Output the generated code to `./Models/IpcModels.g.cs`
+- Use the specified namespace for the generated code
+
+## Creating Bebop Schemas
+
+Bebop uses its own schema language to define data structures. Create a new file with a `.bop` extension (e.g., `schemas.bop`) and define your schemas:
-Any issues encountered while compiling your schemas can now also be inspected from the error list.
-![](https://i.imgur.com/H5jTYtJ.png)
+```bebop
+struct Person {
+ string name;
+ uint32 age;
+}
+
+struct Team {
+ string name;
+ Person[] members;
+}
+```
+
+## Generating C# Code
-## Using Bebop Mirroring
+After defining your schemas, the C# code will be automatically generated when you build your project. Any issues encountered during compilation will be displayed in the error list.
-Mirroring is a Bebop feature supported by the .NET runtime implementation that allows for records to be access and handled dynamically. For instance, you can define a class as a `RecordHandler` and bind it's methods to be invoked whenever a record of a certain type is decoded.
+## Using Generated Code
-It takes advantage of the [opcode](../reference/decorators#opcode) decorator to determine which method to invoke.
+Now you can use the generated code in your C# project. Here's an example of how to create and encode a `Person` object:
```csharp
- [RecordHandler]
- public class ExampleHandler
- {
+using YourNamespace.Models;
+using Bebop.Runtime;
- [BindRecord(typeof(BebopRecord))]
- public async Task HandleChat(object state, ChatMessage chat)
- {
- ...
- }
- }
+// Create a new Person object
+var person = new Person
+{
+ Name = "Spike Spiegel",
+ Age = 27
+};
+
+// Encode the person object to a byte array
+byte[] encoded = BebopSerializer.Encode(person);
+
+// Decode the byte array back to a Person object
+Person decoded = BebopSerializer.Decode(encoded);
+
+Console.WriteLine(decoded.Name); // Output: Spike Spiegel
+Console.WriteLine(decoded.Age); // Output: 27
+```
+
+## Using the BebopSerializer
+
+The `BebopSerializer` class provides static methods for encoding and decoding Bebop records. Here are some examples:
+
+```csharp
+using Bebop.Runtime;
+
+// Encoding
+byte[] encoded = BebopSerializer.Encode(person);
+byte[] encodedWithCapacity = BebopSerializer.Encode(person, 1024); // With initial capacity
+ImmutableArray encodedImmutable = BebopSerializer.EncodeImmutably(person);
+
+// Decoding
+Person decodedFromArray = BebopSerializer.Decode(encoded);
+Person decodedFromSpan = BebopSerializer.Decode(new ReadOnlySpan(encoded));
+Person decodedFromMemory = BebopSerializer.Decode(new ReadOnlyMemory(encoded));
+Person decodedFromImmutable = BebopSerializer.Decode(encodedImmutable);
+```
+
+## Working with Unions
+
+Bebop supports unions, which allow you to define a type that can be one of several possible structures. Here's an example of how to work with unions in C#.
+
+### Defining a Union in Bebop
+
+First, let's look at how a union is defined in a Bebop schema file (`.bop`):
+
+```bebop
+union Person {
+ 1 -> struct John {
+ int32 x;
+ int32 y;
+ }
+ 2 -> struct Doe {
+ int32 age;
+ string name;
+ }
+}
```
-Arbitrary data can then be decoded dynamically by leveraging the records opcode, and the method bound to it is fired.
+
+### Using the Generated Union in C#
+
+After generating the C# code from this Bebop schema, you can use the union like this:
```csharp
-// fired when a message is received from the network.
-public void OnMessage(byte[] data) {
- // decodes our network messages
- var networkMessage = NetworkMessage.Decode(data);
- // decodes and invokes the handler (if any) for the decoded record
- // if the record is ChatMessage then HandleChat is invoked..
- BebopMirror.HandleRecord(networkMessage.IncomingRecordData, networkMessage.IncomingOpcode, stateObject);
+using YourNamespace.Models; // Replace with your actual namespace
+using Bebop.Runtime;
+
+// Creating instances of the union
+var john = new Person(new John { X = 10, Y = 20 });
+var doe = new Person(new Doe { Age = 30, Name = "Jane Doe" });
+
+// Checking the type and accessing fields
+if (john.IsJohn)
+{
+ Console.WriteLine($"John's coordinates: ({john.AsJohn.X}, {john.AsJohn.Y})");
+}
+
+if (doe.IsDoe)
+{
+ Console.WriteLine($"Doe's name: {doe.AsDoe.Name}, Age: {doe.AsDoe.Age}");
+}
+
+// Using pattern matching
+static void PrintPerson(Person person)
+{
+ switch (person.Discriminator)
+ {
+ case 1:
+ var john = person.AsJohn;
+ Console.WriteLine($"John at ({john.X}, {john.Y})");
+ break;
+ case 2:
+ var doe = person.AsDoe;
+ Console.WriteLine($"Doe named {doe.Name}, aged {doe.Age}");
+ break;
+ }
}
+
+// Encoding and decoding unions
+byte[] encodedJohn = BebopSerializer.Encode(john);
+Person decodedJohn = BebopSerializer.Decode(encodedJohn);
+
+// Using the Match method for type-safe handling
+string description = person.Match(
+ john => $"John at ({john.X}, {john.Y})",
+ doe => $"Doe named {doe.Name}, aged {doe.Age}"
+);
+Console.WriteLine(description);
+
+// Using the Switch method for side effects
+person.Switch(
+ john => Console.WriteLine($"Processing John: ({john.X}, {john.Y})"),
+ doe => Console.WriteLine($"Processing Doe: {doe.Name}, {doe.Age}")
+);
```
-:::note
-- **If your `RecordHandler` is non-static the Bebop runtime will create a new instance of that class and hold onto the reference.**
-- **Bound methods currently cannot return any values and use an event driven pattern. Invoked methods do not block.**
-- **Mirroring uses reflection and may not be AOT friendly**
-:::
-:::caution
-Mirroring is a feature that will likely be removed in the future in favor of a C# implementation of [Tempo](../tempo).
-:::
+
+### Key Points About Unions
+
+1. The union type (`Person` in this case) has properties like `IsJohn` and `IsDoe` to check which variant it currently represents.
+2. Use `AsJohn` and `AsDoe` to access the specific fields of each variant.
+3. The `Discriminator` property tells you which variant the union currently represents (1 for John, 2 for Doe in this case).
+4. You can use pattern matching or the `Switch` and `Match` methods for type-safe handling of the union.
+5. Encoding and decoding work seamlessly with unions, just like with regular Bebop structures.
+
+Unions are particularly useful when you need to represent data that can be one of several types, providing a type-safe way to handle different cases in your application.