VB.NET Module member default Public vs Class default Private — controlling implicit visibility
0 reputation · 29 Jan 2021, 21:32 UTC
0 reputation · 29 Jan 2021, 21:32 UTC
VB.NET applies three distinct default accessibility rules: Class and Structure members default to Private, Module and Enum members default to Public, while Interface, Delegate, and top-level Enum declarations default to Friend. This asymmetry is by design for VB6 compatibility, but it creates a silent exposure risk when developers move helper members between a Class and a Module without adding explicit modifiers.
The compiler emits no diagnostic for implicit Public members in a Module, even with Option Strict On. No project-level pragma, /nowarn switch, or .editorconfig rule exists to flip the Module member default to Private or to warn on missing accessibility keywords. Source generators or Roslyn analyzers can surface the issue, but they are opt-in and not part of the standard build.
Partial definitions do not unify the defaults: a member without a modifier in a Partial Class file remains Private, while the same omission in a Partial Module file becomes Public. This means a single logical type split across files can have inconsistent visibility depending on which file a member is declared in.
How can a team enforce explicit accessibility on all Module members without relying on external analyzers? Is there a supported pattern to make Module default visibility match Class behavior at the language or project level? What are the implications for InternalsVisibleTo when a Module’s implicit Public members become visible to friend assemblies?
There is no supported language or project-level switch that makes a VB.NET Module default its members to Private, and the compiler emits no diagnostic for an omitted accessibility keyword. The only built-in control is to write an explicit modifier on every Module member. A team must enforce this through convention and code review, not through Option Strict, .editorconfig, or a compiler flag.
In VB.NET, an unmodified member inside a Module is Public; the same omission inside a Class or Structure is Private. This default behavior is stable across VB.NET compiler releases and is unaffected by Option Strict On, target framework, or language version. Partial files do not unify the defaults: an unmodified member in a Partial Module remains Public, while one in a Partial Class remains Private.
Module member. Use Private for helpers, Friend for assembly-scoped APIs, and Public only when exposure is intentional.Module members..vb files to list Module declarations, then manually inspect those files for members that omit Public, Private, Protected, or Friend.grep -R "\bModule\b" --include="*.vb" src/
InternalsVisibleTo grants a named friend assembly access to Friend members only; it does not restrict Public members. Because an unmodified Module member is Public, it is visible to any code that can reference the assembly, including code in friend assemblies, without being gated by InternalsVisibleTo. If a helper was moved from a Class to a Module without adding a modifier, its visibility silently widened from Private to Public, bypassing any friend-assembly boundary you intended. Move sensitive helpers to Private or Friend, or keep them in a Class where the default is Private.
Compile one Module and one Class, each containing a member with no accessibility keyword, then inspect the assembly with reflection, ILDASM, or ILSpy. The Module member should resolve as Public and the Class member as Private. Rebuild with Option Strict On and confirm the compiler emits no diagnostic for the omitted keyword.
This assumes the standard Roslyn-based VB.NET compiler. If a future compiler release adds a modifier-default switch or warning, this guidance would need review. One missing detail changes the recommendation: are you enforcing explicit modifiers on new code only, or do you also need to audit already-compiled assemblies for implicitly Public Module members? If an audit is required, add a reflection-based scan of existing binaries as a separate step.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.