internal vs public in C
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In C#, public and internal look similar when you are writing code in a single project, but they define very different contracts. public exposes a type or member to any assembly that references yours, while internal keeps it available only inside the current assembly.
What public actually means
A public API is part of the surface area other code can depend on. Once external callers use it, changing it becomes expensive because the change can break consumers you do not control.
In a real library, InvoiceService and ApplyTax could be called from another assembly. That means their names, parameters, and behavior become part of your long-lived contract.
What internal actually means
internal limits visibility to the current assembly. Other projects can reference your library and still not see those members. That makes internal a useful default for helpers, implementation details, and types that are not meant to be part of the product's public API.
TaxFormatter is available inside the assembly, so InvoiceService can use it. External code cannot create or call it directly.
Choose based on API intent, not convenience
The practical rule is simple: expose only what must be called from outside the assembly. If a class exists only to support another public type, make it internal. That reduces coupling and gives you freedom to refactor implementation details later.
This matters most in reusable libraries and large applications split into several projects. In a single small executable, the distinction can feel minor at first, but it becomes important as the codebase grows and boundaries start to matter.
internal helps keep architecture honest
If everything is public, any project can reach into any other project's implementation details. That usually leads to tighter coupling, scattered business rules, and brittle upgrades. Using internal forces outside code to go through a narrower public surface, which is usually where validation, invariants, and backward compatibility should live.
A good API is not the one that exposes the most. It is the one that exposes the least necessary.
Testing internal code
Sometimes you want tests to reach internal types without making them public. C# supports that with InternalsVisibleTo, which grants a specific assembly access to internal members.
That approach is usually better than promoting implementation types to public just for test convenience. Tests get the access they need, while production consumers still see a smaller API.
Nested visibility still matters
Remember that visibility is checked at each level. A public method that returns an internal type is not usable as an external API because the returned type itself is hidden. The compiler helps catch many of these inconsistencies, but the design lesson is broader: your public surface should be coherent end to end.
Common Pitfalls
- Making helper classes
publiconly because another file in the same project needs them. - Treating
publicas the default and accidentally committing to APIs you wanted to keep flexible. - Exposing
publicmembers that referenceinternaltypes, which creates inconsistent visibility. - Making types
publicfor tests instead of usingInternalsVisibleTowhen appropriate. - Forgetting that assembly boundaries, not folders or namespaces, determine
internalaccess.
Summary
- '
publicexposes types and members to any referencing assembly.' - '
internalrestricts access to the current assembly.' - Default to the narrowest visibility that still supports the design.
- A smaller public API is easier to maintain and refactor.
- Use
InternalsVisibleTowhen tests need access to internal implementation details.
Related reading
- InternalsVisibleTo attribute isn't working
- Intersection of multiple lists with IEnumerable.Intersect
- IntPtr, SafeHandle and HandleRef - Explained
- Invalid cast from ''System.Int32'' to ''System.Nullable1System.Int32, mscorlib
- Invalid postback or callback argument. Event validation is enabled using 'pages enableEventValidationtrue/
- Invalid token 'void' when compling async..await on build server
- InvalidOperationException Unable to resolve service for type 'Microsoft.AspNetCore.Http.IHttpContextAccessor
- Invoke or BeginInvoke cannot be called on a control until the window handle has been created

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.