What are the key differences between static classes in C# versus Java?

I’m working on implementing a builder pattern in C# after seeing it work great in Java. I found this pattern in a programming book and wanted to try it out in my project.

Here’s my working Java version:

public class Vehicle {
    private String model;
    private String brand;
    private int capacity = 0;

    public static void main(String[] args) {
        Vehicle newVehicle = new Vehicle.Creator("SUV Model").Brand("Toyota").Capacity(5).create();
    }

    private Vehicle(Creator creator) {
        this.model = creator.model;
        this.brand = creator.brand;
        this.capacity = creator.capacity;
    }

    public static class Creator {
        private String model;
        private String brand;
        private int capacity = 0;

        public Creator(String model) {
            this.model = model;
        }

        public Creator Brand(String brand) {
            this.brand = brand;
            return this;
        }

        public Creator Capacity(int capacity) {
            this.capacity = capacity;
            return this;
        }

        public Vehicle create() {
            return new Vehicle(this);
        }
    }
}

But when I try the same approach in C#:

public class Vehicle
{
    private String model;
    private String brand;

    private Vehicle(Creator creator)
    {
        model = creator.model;
        brand = creator.brand;
    }

    public static class Creator
    {
        private String model;
        private String brand;
        private int capacity = 0;

        public Creator(String val)
        {
            this.model = val;
        }

        public Creator Brand(String val)
        {
            this.brand = val;
            return this;
        }

        public Creator Capacity(int val)
        {
            this.capacity = val;
            return this;
        }

        public Vehicle create()
        {
            return new Vehicle(this);
        }
    }
}

I get compiler errors saying I can’t declare instance members in a static class. What am I doing wrong here? Can this pattern work in C# the same way it does in Java? Also wondering if this approach is thread safe when multiple users access it.

yeah this tripped me up too when switching from java. c# static classes are way more restrictive - they cant have constructors or instance stuff at all. just remove ‘static’ from Creator and you’re good to go, it’ll still work the same way from outside.

The core issue you’re encountering stems from a fundamental difference in how C# and Java handle static classes. In Java, static nested classes can have instance members and constructors - they’re essentially regular classes that don’t need a reference to the outer class instance. However, C# static classes are completely static and cannot contain any instance members, constructors, or be instantiated.

For the builder pattern in C#, you should use a regular nested class instead of a static one. Simply remove the static keyword from your Creator class declaration. The class can still be accessed as Vehicle.Creator since it’s nested, but now it can have instance members and constructors as needed.

Regarding thread safety, your current implementation isn’t thread-safe in either language since you’re using instance fields that could be modified between method calls. If you need thread safety, consider making the builder immutable by creating new instances on each method call rather than modifying existing state.

You’ve hit one of the most confusing differences between these two languages. Java’s static nested classes are misleadingly named because they can actually be instantiated and have instance members. In C#, when you mark a class as static, it truly means everything inside must be static too - no instances allowed whatsoever.

The solution is straightforward: drop the static keyword from your Creator class in C#. You’ll still access it the same way through Vehicle.Creator, but now the compiler will allow constructors and instance fields. I ran into this exact same issue when porting some Java code last year and spent way too much time scratching my head over it.

One thing worth mentioning about your thread safety concern - both versions have the same problem where multiple threads could potentially interfere with each other if they’re modifying the same builder instance simultaneously. Each thread should create its own builder instance to avoid any race conditions.