Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A public field in C# is a variable declared directly within the memory layout of a class or struct using the public access modifier, exposing raw data directly to calling code without encapsulation. It allocates memory for state storage at the instance level (or type level if marked static) and, unless marked readonly or const, permits unrestricted read and write operations from any assembly referencing the type.

Syntax and Declaration

A public field is declared by specifying the public access modifier, followed by the data type, and the field identifier.
public class ObjectModel
{
    // Public instance field
    public int InstanceIdentifier; 

    // Public static field
    public static string TypeIdentifier; 

    // Public readonly field
    public readonly double FixedValue = 3.14159; 
}

Technical Characteristics

  • Memory Allocation: For reference types (class), public instance fields are allocated on the managed heap as part of the object’s contiguous memory block. For value types (struct), they are allocated inline wherever the struct is allocated (stack or heap). The allocation of static public fields depends on their type:
    • GC Statics: Reference types or value types containing references are allocated on the managed GC heap (typically within an object array referenced by the type’s MethodTable) so the Garbage Collector can trace them.
    • Non-GC Statics: Primitives (like int, bool) or simple structs without references are allocated in unmanaged memory (specifically the Loader Heap / DomainLocalModule) because they do not require GC tracking.
  • Default Initialization: The Common Language Runtime (CLR) automatically zero-initializes public fields upon memory allocation. They are assigned the default value of their respective types (e.g., 0 for numeric types, false for booleans, null for reference types) before any constructor logic executes.
  • Mutability: By default, public fields are fully mutable. Any external code with a reference to the instance (or the type, for static fields) can overwrite the memory location.
  • Binary Compatibility (ABI): Public fields are baked directly into the Application Binary Interface (ABI). If a public field is later refactored into a public property, it constitutes a breaking change at the binary level. Dependent assemblies must be recompiled because the Intermediate Language (IL) instructions for accessing a field (ldfld, stfld) differ entirely from those used to access a property (callvirt for instance properties on reference types, or call for static properties and instance properties on value types).

Modifiers Applied to Public Fields

Public fields can be combined with specific modifiers to alter their runtime behavior:
  • readonly: Restricts mutation. The field can only be assigned a value at the point of declaration, within the constructor of the defining type, or within an init accessor (introduced in C# 9.0).
  • static: Binds the field to the type object itself rather than individual instances. For generic types, a distinct copy of a static field exists for each closed constructed type (e.g., MyClass<int> and MyClass<string> have completely separate static fields). Additionally, if the field is decorated with the [ThreadStatic] attribute, a separate copy is maintained per thread.
  • const: Creates a compile-time constant. The field is implicitly static. For primitive value types, the value is embedded directly into the IL of the calling code, meaning no memory is allocated for it at runtime. However, for string constants, the string data is stored in assembly metadata and allocated on the managed heap (within the intern pool) at runtime when the ldstr instruction is executed.
  • volatile: Instructs the JIT compiler to emit memory barriers (acquire/release semantics) that prevent instruction reordering and prevents the compiler from enregistering the variable (caching it in a CPU register for a loop). Visibility across CPU cores is handled by hardware cache coherency protocols (such as MESI), meaning the CPU still utilizes L1/L2/L3 hardware caches rather than bypassing them to hit physical RAM.

Distinction from Properties

While syntactically similar at the call site, public fields are fundamentally different from public properties at the compiler, CLR, and ecosystem levels:
public class DataContainer
{
    public int MyField;      // Raw memory location
    public int MyProperty { get; set; } // Syntactic sugar for get_MyProperty(), set_MyProperty(), AND a hidden compiler-generated backing field
}
  • Data Binding and Serialization: Most .NET UI frameworks (WPF, WinForms, MAUI, Blazor) and Object-Relational Mappers (like Entity Framework) are architected to bind and map exclusively to properties. They often completely ignore or lack support for public fields, making properties the required mechanism for state that interacts with these subsystems.
  • Interfaces: Public instance fields cannot be declared in interface definitions, as interfaces define behavioral contracts (methods/properties) rather than instance memory layout. However, since C# 8.0, interfaces are permitted to declare public static fields.
  • Reflection: Fields and properties are queried using different reflection APIs (Type.GetField() vs Type.GetProperty()).
  • Attributes: Attributes targeting fields (such as [NonSerialized]) apply directly to the field declaration. When applying such attributes to the hidden backing field of an auto-property, the field: target specifier (e.g., [field: NonSerialized]) must be used to explicitly instruct the compiler to apply the attribute to the backing field rather than the property itself.
Master C# with Deep Grasping Methodology!Learn More