1.
Constants
A constant is
a symbol that has a never-changing value. When defining a constant
symbol, its value must be determinable at compile time.
The compiler then saves the constant’s value in the assembly’s metadata.
This means that you can define a constant only for types that your compiler
considers primitive types. In C#, the following types are primitives and can
be used to define constants: Boolean, Char, Byte,
SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double,
Decimal, and String.
However, C# also allows you to define a constant variable of a
non-primitive type if you set the value
to null:
using System;
public sealed class SomeType {
// SomeType is not a primitive type but C# does
allow
// a constant variable of this type to be set to
‘null‘.
public const SomeType Empty = null;
}
When
code refers to a constant symbol, compilers look up the symbol in the metadata
of the assembly that defines the constant, extract the constant’s value,
and embed the value in the emitted Intermediate Language (IL) code. Because
a constant’s value is embedded directly in code, constants don’t require
any memory to be allocated for them at runtime.
If
the developer changes the constant and only rebuilds the DLL assembly, the
application assembly which refers the constant is not affected. For the
application to pick up the new value, it will have to be recompiled as
well.
2.
Fields
A
field is a data member that holds an instance of a value type or a reference to
a reference type.
Filed modifiers
CLR Term C# Term
Static
static
The field is part of the type’s state, as opposed to
being part of an object’s state.
Instance (default)
The field is associated with an instance of the
type, not the type itself.
InitOnly
readonly
The field can be written to only
by code contained in a constructor method.
Volatile
volatile
Code that
accessed the field is not subject to some thread-unsafe optimizations that
may be performed
by the compiler, the CLR, or by hardware. Only the following types can
be marked volatile: all reference
types, Single,
Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Char, and all
enumerated types with
an underlying type of Byte, SByte, Int16, UInt16, Int32, or
UInt32.
For
type fields, the dynamic memory required to hold the field’s data
is allocated inside the type object, which is created when the type is loaded
into an AppDomain, which typically happens the first time any method
that references the type is just-in-time (JIT)–compiled.
For
instance fields, the dynamic memory to hold the field is allocated
when an instance of the type is constructed.
Because
fields are stored in dynamic memory, their value can be obtained at runtime
only. Fields also solve the versioning problem that exists with constants.
In addition, a field can be of any data type, so you don’t have to restrict
yourself to your compiler’s built-in primitive types (as you do for
constants).