VarDump vs ObjectDumper.Net

CSharp · Console

/Program.cs

// @nuget: ObjectDumper.NET -Version 4.3.2
// @nuget: VarDump -Version 2.0.9

using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using VarDump;
using VarDump.Visitor;
using VarDump.Visitor.Format;

const int MaxOutputLength = 2_000;

var people = new[]
{
    new Person("Alice", 32),
    new Person("Bob", 29),
    new Person("Carla", 41)
};

var order = new Order
{
    Number = "ORD-2026-42",
    CreatedAt = new DateTimeOffset(2026, 7, 26, 10, 30, 0, TimeSpan.FromHours(2)),
    Customer = people[0],
    Items =
    [
        new OrderItem("Keyboard", 1, 89.99m),
        new OrderItem("Mouse", 2, 39.50m)
    ]
};

Compare(
    "1. Typical object graph",
    order,
	true,
    "CS8386: `new dynamic { ... }` is invalid object creation; compilation fails before any conversion to IReadOnlyList<OrderItem> can be considered."
    );

Compare(
    "2. Collections and arrays",
    new
    {
        ReadOnly = Array.AsReadOnly(people),
        Immutable = people.ToImmutableArray(),
        Frozen = people.ToFrozenDictionary(person => person.Name),
        Matrix = new[,]
        {
            { 1, 2, 3 },
            { 4, 5, 6 }
        }
    },
	false,
    "CS7036 and CS1061: ReadOnlyCollection<Person> has neither a parameterless constructor nor an Add method for this collection initializer.",
    "CS0246: the emitted LengthBucketsFrozenDictionary<Person> runtime type cannot be resolved from consumer code (the actual type is non-public).",
    "CS0846 for every scalar: int[,] requires nested row initializers, but ObjectDumper emits a flat initializer.");

Console.WriteLine();
Console.WriteLine(
    "  ! ImmutableArray<Person> compiles, but each collection-initializer Add result is discarded; " +
    "the expression reconstructs an empty default ImmutableArray instead of the original values.");

Section("3. VarDump: modern C# syntax");

var modernOptions = new VarDump.Visitor.DumpOptions
{
    CollectionLiteralStyle = CollectionLiteralStyle.Expression,
    StringLiteralStyle = StringLiteralStyle.Raw,
    PrimitiveCollectionLayout = CollectionLayout.SingleLine,
    IgnoreNullValues = false,
    IgnoreDefaultValues = false
};

Print(
    "Collection expressions, raw strings, nulls, and defaults",
    () => new CSharpDumper(modernOptions).Dump(new
    {
        Message = "Line one\n\"quoted text\"\nLine three",
        Values = new[] { 1, 2, 3 },
        Optional = (string?)null,
        Count = 0
    }));

Section("4. VarDump: formatting and limits");

Print(
    "Hexadecimal formatting",
    () => new CSharpDumper(new VarDump.Visitor.DumpOptions
    {
        IntegralNumericFormat = "X8_4",
        PrimitiveCollectionLayout = CollectionLayout.SingleLine,
        UsePredefinedConstants = false
    }).Dump(new[] { 15, 255, 65_535 }));

Print(
    "Collection size limit",
    () => new CSharpDumper(new VarDump.Visitor.DumpOptions
    {
        MaxCollectionSize = 3,
        PrimitiveCollectionLayout = CollectionLayout.SingleLine
    }).Dump(Enumerable.Range(1, 10)));

Print(
    "DateTime constructor with named arguments",
    () => new CSharpDumper(new VarDump.Visitor.DumpOptions
    {
        DateTimeInstantiation = DateTimeInstantiation.New,
        GenerateVariableInitializer = false,
        UseNamedArgumentsInConstructors = true
    }).Dump(new DateTime(2026, 7, 26, 10, 30, 15, DateTimeKind.Utc)));

Section("5. VarDump: Visual Basic output");

Print(
    "Visual Basic",
    () => new VisualBasicDumper().Dump(new
    {
        Name = "Alice",
        Active = true,
        Values = new[] { 1, 2, 3 }
    }));

Section("6. Different output APIs");

Console.WriteLine();
Console.WriteLine("--- VarDump / direct TextWriter output ---");

new CSharpDumper(new VarDump.Visitor.DumpOptions
{
    PrimitiveCollectionLayout = CollectionLayout.SingleLine
}).Dump(new[] { "one", "two", "three" }, Console.Out);

Console.WriteLine();

Print(
    "ObjectDumper.NET / console-style output",
    () => ObjectDumper.Dump(order));

void Compare(
    string title,
    object value,
	bool setPropertiesOnly = true,
    params string[] objectDumperCompilationProblems)
{
    Section(title);

    Print(
        "VarDump / C#",
        () => new CSharpDumper().Dump(value));

    Print(
        "ObjectDumper.NET / C#",
        () => ObjectDumper.Dump(value, new DumpOptions
        {
            DumpStyle = DumpStyle.CSharp,
            SetPropertiesOnly = setPropertiesOnly
        }));

    if (objectDumperCompilationProblems.Length == 0)
    {
        return;
    }

    Console.WriteLine();
    Console.WriteLine("ObjectDumper C# compilation problems:");

    foreach (var problem in objectDumperCompilationProblems)
    {
        Console.WriteLine($"  ✗ {problem}");
    }
}

void Print(string title, Func<string> createOutput)
{
    Console.WriteLine();
    Console.WriteLine($"--- {title} ---");

    try
    {
        var output = createOutput() ?? "<null>";

        if (output.Length > MaxOutputLength)
        {
            Console.WriteLine(output[..MaxOutputLength]);
            Console.WriteLine($"... truncated after {MaxOutputLength:N0} characters ...");
            return;
        }

        Console.WriteLine(output);
    }
    catch (Exception exception)
    {
        Console.WriteLine($"{exception.GetType().Name}: {exception.Message}");
    }
}

void Section(string title)
{
    Console.WriteLine();
    Console.WriteLine(new string('=', 72));
    Console.WriteLine(title);
    Console.WriteLine(new string('=', 72));
}

public sealed record Person(string Name, int Age);

public sealed class Order
{
    public string Number { get; init; } = "";
    public DateTimeOffset CreatedAt { get; init; }
    public Person Customer { get; init; } = new("", 0);
    public IReadOnlyList<OrderItem> Items { get; init; } = Array.Empty<OrderItem>();

    public decimal Total => Items.Sum(item => item.Quantity * item.UnitPrice);
}

public sealed record OrderItem(string Name, int Quantity, decimal UnitPrice);
;