Access modifiers
public, private, internal, protected and protected internal define API boundaries for declarations and struct members.
Default visibility
Namespace-level functions and struct members are private by default. Writing private makes that default explicit; writing public exposes the declaration outside its owning namespace or struct.
namespace Library;
int CalculateCore() { return 40; } // private by default
private int InternalValue() { return 2; } // explicitly private
public int Calculate() { return CalculateCore() + InternalValue(); }
Code in another namespace may call Library.Calculate(). Private functions remain available to other declarations inside Library.
Project and library visibility
internal exposes a declaration only to semantic code from the same Xenon project or compiled XELIB. One .xeproj is one source boundary; one XELIB identity is one compiled-library boundary. A project reference and an XELIB consumer are outside that boundary.
internal struct BufferState {}
internal int FormatCore() { return 42; }
public int Format() { return FormatCore(); }
Top-level structs, interfaces, enums, templates, functions and constants may be internal. Internal declarations needed by public generic implementations remain usable by those implementations after XELIB loading, but are not imported into the consumer API.
Inheritance visibility
protected members are accessible in their declaring struct and derived structs. For access through an explicit receiver in a derived struct, the receiver must have that derived type (or a type derived from it); merely holding a base-typed value does not grant access.
public struct Base
{
protected int Value;
protected Base(int value) { Value = value; }
}
public struct Derived : Base
{
public int Read() { return Value; }
}
protected internal uses OR semantics: access is granted when either the internal-boundary rule or the inheritance rule succeeds.
| Caller | internal | protected | protected internal |
|---|---|---|---|
| Same library, derived | Yes | Yes | Yes |
| Same library, unrelated | Yes | No | Yes |
| Different library, derived | No | Yes | Yes |
| Different library, unrelated | No | No | No |
protected internal overrides
Override accessibility is evaluated at the library boundary. Inside the same library, both branches of protected internal are available, so an override must remain protected internal or widen to public. In a different library, only the protected branch crosses the boundary; a derived type may therefore use protected override, protected internal override or public override. An external internal or private override is not permitted.
protected and protected internal are member modifiers; top-level types and enum static fields cannot use them.
Inconsistent accessibility
An API cannot expose a nominal type that is less accessible than the declaration exposing it. The rule applies to return and parameter types, fields, properties, indexers, constructors, base types, implemented interfaces and generic constraints.
internal struct Hidden {}
public Hidden GetHidden() // Error: Hidden is not public
{
return Hidden();
}
internal Hidden GetInternal() // Valid: both stay inside the library
{
return Hidden();
}
Wrapping does not hide the exposed type. Xenon checks through pointers, references, arrays, ownership and lifetime wrappers, atomics, constructed generic arguments, and function-pointer return and parameter types.
The containing declaration limits effective visibility. A public member inside an internal struct is effectively internal, so it may use another internal type from the same library without creating an invalid public surface. This validation runs before a XELIB can be emitted.
Struct members
The same modifiers apply to fields, methods, constructors, destructors, properties, indexers and static members. Private members are accessible from code belonging to the declaring struct; public members form its external API.
struct Counter
{
private int value;
public Counter(int initialValue)
{
value = initialValue;
}
public int Value
{
get { return value; }
}
private void ResetCore()
{
value = 0;
}
}
Positional brace construction follows the same visibility rules. Code outside Counter cannot write value through Counter { ... }; it must call the public constructor or another public factory supplied by the type.
Destructor visibility controls both explicit delete and automatic local/stack-array cleanup. Storage-only free never invokes a destructor. A private destructor is callable only from its own struct, and an override of a public virtual destructor must remain public. See destructor access modifiers for release APIs, inherited cleanup and examples.
Visibility and native linkage
public controls visibility inside Xenon source code. export additionally makes a function public and exposes it as a native symbol, while extern declares a native function implemented elsewhere.
public int Add(int a, int b) { return a + b; }
export int NativeAdd(int a, int b) { return a + b; }
public alone retains the dotted Xenon symbol name; it does not request C export naming or a Windows DLL export. An extern declaration is private by default and can use public extern for access from other namespaces. See native names and export visibility.