Main method
private method
Java programming
access modifiers
coding best practices

Why is Main method private?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In C#, the Main method can be private because the runtime does not use normal access modifier rules to find and invoke it. The CLR (Common Language Runtime) locates Main by searching for a static method named Main with the correct signature, regardless of its access level. Making Main private prevents other classes from calling it directly, which is appropriate because it is an entry point, not a reusable API. In Java, main must be public because the JVM specification requires it.

C#: Main Can Be Private

csharp
1// This compiles and runs correctly
2class Program
3{
4    private static void Main(string[] args)
5    {
6        Console.WriteLine("Hello from private Main!");
7    }
8}

The C# compiler and CLR find Main by its name and signature, not by its accessibility. The access modifier has no effect on whether the program starts — the runtime bypasses access checks for the entry point.

Why Private Makes Sense

csharp
1// Main is NOT meant to be called by other code
2class Program
3{
4    private static void Main(string[] args)
5    {
6        // Application bootstrap — not a reusable method
7        var host = CreateHostBuilder(args).Build();
8        host.Run();
9    }
10
11    private static IHostBuilder CreateHostBuilder(string[] args) =>
12        Host.CreateDefaultBuilder(args)
13            .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
14}
15
16// If Main were public, another class could accidentally call:
17// Program.Main(new string[] { });  // Re-entering the application — almost always a bug

Making Main private prevents accidental re-invocation from other parts of the codebase.

Java: main Must Be Public

java
1// Java requires public — this is enforced by the JVM
2public class App {
3    public static void main(String[] args) {
4        System.out.println("Hello from Java!");
5    }
6}
java
1// BROKEN — JVM cannot find the entry point
2public class App {
3    private static void main(String[] args) {  // Error at runtime
4        System.out.println("This won't run");
5    }
6}
7// Error: Main method not found in class App

The JVM specification requires main to be public static void main(String[]). Unlike C#'s CLR, the JVM respects access modifiers when searching for the entry point.

C# Valid Main Signatures

csharp
1// All of these are valid entry points in C#
2
3// Standard
4static void Main(string[] args) { }
5
6// No args
7static void Main() { }
8
9// Return int (exit code)
10static int Main(string[] args) { return 0; }
11
12// Async (C# 7.1+)
13static async Task Main(string[] args) { await Task.CompletedTask; }
14
15// Async with return code
16static async Task<int> Main(string[] args) { return 0; }
17
18// Any access modifier works
19private static void Main(string[] args) { }
20internal static void Main(string[] args) { }
21public static void Main(string[] args) { }
22protected static void Main(string[] args) { }  // Unusual but valid

C# Top-Level Statements (C# 9+)

In C# 9+, you can skip Main entirely:

csharp
1// Program.cs — no class, no Main method
2Console.WriteLine("Hello, World!");
3
4// The compiler generates a hidden Main method:
5// internal class Program
6// {
7//     private static void Main(string[] args)
8//     {
9//         Console.WriteLine("Hello, World!");
10//     }
11// }

The compiler-generated Main is private by default — further evidence that the runtime does not require public.

Multiple Main Methods

csharp
1class Foo
2{
3    static void Main(string[] args)
4    {
5        Console.WriteLine("Foo.Main");
6    }
7}
8
9class Bar
10{
11    static void Main(string[] args)
12    {
13        Console.WriteLine("Bar.Main");
14    }
15}

If multiple classes define Main, the compiler requires you to specify which one to use:

xml
1<!-- .csproj -->
2<PropertyGroup>
3    <StartupObject>Foo</StartupObject>
4</PropertyGroup>

Or from the command line:

bash
csc -main:Foo Program.cs

Comparison Across Languages

LanguageEntry PointAccess ModifierWhy
C#static Main()Any (private OK)CLR ignores access modifiers for entry point
Javastatic main()Must be publicJVM spec requires public
Kotlinfun main()Top-level functionNo class needed, no modifier
C/C++int main()N/A (no access modifiers at file scope)Linker finds symbol by name
PythonNo special entryN/AScript runs top to bottom
Gofunc main()Exported by conventionMust be in package main

Common Pitfalls

  • Assuming Main must be public in C#: The CLR does not require any specific access modifier. Making Main public is convention, not requirement. Private is equally valid and arguably better practice for preventing accidental invocation.
  • Trying private main in Java: The JVM will throw Error: Main method not found in class at runtime. Java strictly enforces public static void main(String[] args). Even protected or package-private will not work.
  • Multiple Main methods without specifying startup object: If multiple classes in a C# project define Main, the build fails with CS0017: Program has more than one entry point defined. Set <StartupObject> in the project file.
  • Confusing entry point discovery with reflection: The runtime's ability to call a private Main is not the same as reflection. It is a special runtime behavior for application startup only. Private methods are still inaccessible to other code through normal compilation.
  • Thinking Main access modifier affects security: Making Main private does not provide meaningful security. It prevents source-level calls from other classes, but reflection can always invoke private methods. The primary benefit is API cleanliness.

Summary

  • C# Main can be private because the CLR finds the entry point by name and signature, ignoring access modifiers
  • Java main must be public because the JVM specification requires it
  • Private Main prevents other classes from accidentally calling the entry point directly
  • C# 9+ top-level statements generate a private Main automatically
  • Valid C# Main signatures include void, int, Task, and Task<int>, with or without string[] args
  • The access modifier choice is about API design, not security or runtime behavior

Course illustration
Course illustration

All Rights Reserved.