Local helper methods
Not every repeated snippet deserves a full component. Often you just want a local shortcut (a small private method, or even a local function), that removes noise from a single document-building method. This pattern is about readability at the smallest scale.
The problem: repetition inside one method
Section titled “The problem: repetition inside one method”Without helpers, a method that builds a styled section repeats the same incantation:
section.Add(new Paragraph("Total revenue") { Style = new Style().Bold().WithForegroundColor(Color.FromRgb(0, 90, 160)) });section.Add(new Paragraph("Net profit") { Style = new Style().Bold().WithForegroundColor(Color.FromRgb(0, 90, 160)) });section.Add(new Paragraph("Operating margin") { Style = new Style().Bold().WithForegroundColor(Color.FromRgb(0, 90, 160)) });Local functions for one-method shortcuts
Section titled “Local functions for one-method shortcuts”When the shortcut is only needed inside a single method, a local function keeps it right where it’s used: no new class member, no scrolling:
public Document BuildSummary(Metrics m){ var doc = Document.Create("Summary"); var section = doc.AddSection();
var accent = Color.FromRgb(0, 90, 160); Paragraph Metric(string label) => new Paragraph(label) { Style = new Style().Bold().WithForegroundColor(accent) };
section.Add(Metric("Total revenue")); section.Add(Metric("Net profit")); section.Add(Metric("Operating margin"));
return doc;}The local function Metric captures accent from the enclosing scope, so the
shared color is defined once and the call sites stay short.
Private methods for cross-method shortcuts
Section titled “Private methods for cross-method shortcuts”When the same shortcut is useful across several building methods in the same class,
promote it to a private method:
public sealed class InvoiceBuilder{ private static readonly Color Accent = Color.FromRgb(0, 90, 160);
private static Paragraph Label(string text) => new Paragraph(text) { Style = new Style().Bold().WithForegroundColor(Accent) };
private static Paragraph Value(string text, TextAlignment align = TextAlignment.Right) => new Paragraph(text) { Style = new Style().WithTextAlignment(align) };
public Document Build(Invoice invoice) { var doc = Document.Create($"Invoice {invoice.Number}"); var section = doc.AddSection();
section.Add(Label("Bill to")); section.Add(Value(invoice.CustomerName, TextAlignment.Left)); // ... return doc; }}Helpers that compute, not just format
Section titled “Helpers that compute, not just format”Helpers aren’t only for styling. A common use is folding repetitive content logic ( formatting a currency, building a key/value row) into one place:
private static TableRow MoneyRow(string label, decimal amount) => new TableRow() .AddCell(label) .AddCell(amount.ToString("C"));
var table = new Table().Header("Item", "Amount");table.AddRow(MoneyRow("Subtotal", invoice.Subtotal));table.AddRow(MoneyRow("Tax", invoice.Tax));table.AddRow(MoneyRow("Total", invoice.Total));When to use which
Section titled “When to use which”| Reach for… | When |
|---|---|
| Local function | The shortcut is used in exactly one method |
| Private method | The shortcut is shared across methods in one class |
| Component | The piece is a meaningful, reusable unit of the document |
| Extension method | You want a fluent shortcut available across the whole codebase |
These are points on a spectrum from local to global. Start local (a local function costs nothing and is the easiest to delete) and promote only when reuse actually materializes.
Guidelines
Section titled “Guidelines”- Prefer the smallest scope that removes the duplication.
- Capture shared constants (colors, fonts, indents) once and let helpers close over them.
- Name by intent (
Label,MoneyRow), not by mechanics (MakeBoldBlueParagraph). - Don’t over-abstract a helper used twice; two call sites is enough, three is plenty.