C# Reference

Type Mappings

IDL Type C# Type Example
string string "hello"
int long 42L
float double 3.14
bool bool true, false
[]Type List<Type> new List<int> { 1, 2, 3 }
map[string]Type Dictionary<string, Type> new Dictionary<string, Type>
Enum enum OrderStatus.Pending
Struct Class with properties new Product { ... }
T [optional] Nullable<T> or default null or default

Generated Classes

Each struct in your IDL becomes a C# class:

using Checkout;

// Create instances using object initializer
var product = new Product {
    ProductId = "prod001",
    Name = "Wireless Mouse",
    Description = "Ergonomic mouse",
    Price = 29.99,
    Stock = 50,
    ImageUrl = "https://example.com/mouse.jpg"  // optional field
};

var cart = new Cart {
    CartId = "cart_1234",
    Items = new List<CartItem>(),
    Subtotal = 0.0
};

Optional Fields

Optional fields can be null:

// Create with optional field
var product = new Product {
    ProductId = "prod001",
    Name = "Wireless Mouse",
    Price = 29.99,
    Stock = 50,
    ImageUrl = null  // optional field can be null
};

// Check optional field
if (product.ImageUrl != null) {
    Console.WriteLine(product.ImageUrl);
}

Enums

Enums use proper C# enum with constants:

using Checkout;

// Use enum constants
var order = new Order {
    OrderId = "order_123",
    Cart = cart,
    ShippingAddress = address,
    PaymentMethod = PaymentMethod.CreditCard,
    Status = OrderStatus.Pending,
    Total = 59.98,
    CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};

// Compare enums
if (order.Status == OrderStatus.Pending) {
    Console.WriteLine("Order is pending");
}

Error Handling

Throw RPCError with custom codes:

using PulseRPC;

// Standard JSON-RPC errors
throw new RPCError(-32602, "Invalid params");

// Custom application errors (use codes >= 1000)
throw new RPCError(1001, "CartNotFound: Cart does not exist");
throw new RPCError(1002, "CartEmpty: Cannot create order from empty cart");

Common error codes:

Server Implementation

Implement generated interfaces:

using PulseRPC;
using Checkout;

class CatalogServiceImpl : ICatalogService {
    private List<Product> products = new List<Product> {
        new Product { ProductId = "p1", Name = "Item 1", Price = 10.0, Stock = 5 },
        new Product { ProductId = "p2", Name = "Item 2", Price = 20.0, Stock = 3 }
    };

    public List<Product> ListProducts() {
        return products;
    }

    public Product GetProduct(string productId) {
        return products.FirstOrDefault(p => p.ProductId == productId);
    }
}

// Start server
class Program {
    static async Task Main(string[] args) {
        var server = new PulseRPCServer();
        server.RegisterCatalogService(new CatalogServiceImpl());
        await server.RunAsync("localhost", 8080);
    }
}

Client Usage

using PulseRPC;
using Checkout;

var transport = new HttpTransport("http://localhost:8080");
var catalog = new CatalogServiceClient(transport);

// Method calls return C# objects
var products = catalog.ListProducts();
foreach (var p in products) {
    Console.WriteLine($"{p.Name} - ${p.Price}");
}

// Optional methods return null if not found
var product = catalog.GetProduct("prod001");
if (product != null) {
    Console.WriteLine(product.Name);
}

Async/Await Pattern

PulseRPC C# supports async/await:

using System.Threading.Tasks;

class OrderServiceImpl : IOrderService {
    public async Task<CheckoutResponse> CreateOrder(CreateOrderRequest request) {
        // Async operations
        var orderId = await GenerateOrderIdAsync();
        var validated = await ValidateCartAsync(request.CartId);

        if (!validated) {
            throw new RPCException(1002, "CartEmpty: Cannot create order from empty cart");
        }

        return new CheckoutResponse { OrderId = orderId };
    }
}

Validation

PulseRPC automatically validates:

// This will throw RPCException (-32602) if validation fails
var cart = cart.AddToCart(new AddToCartRequest {
    CartId = null,
    ProductId = "prod001",
    Quantity = 2
});

LINQ Integration

Use LINQ for working with collections:

using System.Linq;

// Filter
var inStockProducts = products.Where(p => p.Stock > 0).ToList();

// Project
var productNames = products.Select(p => p.Name).ToList();

// Find
var firstProduct = products.FirstOrDefault(p => p.ProductId == "prod001");

// Aggregate
var totalValue = products.Sum(p => p.Price * p.Stock);

Best Practices

  1. Use object initializers: Cleaner than constructor parameters
  2. Null check optionals: Always check for null on optional fields
  3. Use LINQ: Elegant collection manipulation
  4. Async for I/O: Use async/await for database/network calls
  5. Dependency injection: Inject services into constructors

Working with Nested Structs

// Nested structs work naturally
var order = new Order {
    OrderId = "order_123",
    Cart = new Cart {
        CartId = "cart_123",
        Items = new List<CartItem> { new CartItem { ... } },
        Subtotal = 59.98
    },
    ShippingAddress = new Address {
        Street = "123 Main St",
        City = "San Francisco",
        State = "CA",
        ZipCode = "94105",
        Country = "USA"
    },
    PaymentMethod = PaymentMethod.CreditCard,
    Status = OrderStatus.Pending,
    Total = 59.98,
    CreatedAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};

.NET Project Integration

Generated code includes .csproj file:

# Build
dotnet build

# Run server
dotnet run --project Server.csproj

# Run client
dotnet run --project Client.csproj

Using with ASP.NET Core

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api")]
public class CheckoutController : ControllerBase {
    private readonly ICartService _cartService;

    public CheckoutController(ICartService cartService) {
        _cartService = cartService;
    }

    [HttpPost("cart")]
    public Cart AddToCart([FromBody] AddToCartRequest request) {
        return _cartService.AddToCart(request);
    }
}

Dependency Injection

using Microsoft.Extensions.DependencyInjection;

// Startup.cs
public void ConfigureServices(IServiceCollection services) {
    services.AddSingleton<ICatalogService, CatalogServiceImpl>();
    services.AddSingleton<ICartService, CartServiceImpl>();
    services.AddSingleton<IOrderService, OrderServiceImpl>();
}

// Controller
public class CartController : ControllerBase {
    private readonly ICartService _cartService;

    public CartController(ICartService cartService) {
        _cartService = cartService;
    }
}

Configuration

Add to appsettings.json:

{
  "PulseRPC": {
    "ServerUrl": "http://localhost:8080",
    "Timeout": 30000
  }
}

Load in startup:

var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

var serverUrl = config["PulseRPC:ServerUrl"];

Exception Handling

try {
    var response = orders.CreateOrder(request);
    Console.WriteLine($"Order created: {response.OrderId}");
}
catch (RPCException ex) {
    // Handle application errors
    Console.WriteLine($"Error {ex.Code}: {ex.Message}");
}
catch (Exception ex) {
    // Handle system errors
    Console.WriteLine($"System error: {ex.Message}");
}

Serialization

PulseRPC C# uses System.Text.Json or Newtonsoft.Json:

using System.Text.Json;

// Custom serialization options
var options = new JsonSerializerOptions {
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true
};

var json = JsonSerializer.Serialize(product, options);