Ultimately, they are just a generic struct with a bool flag - except with special boxing rules. Because structs are (by default) initialized to zero, the bool defaults to false (no value):
public struct Nullable<T> where T : struct { private readonly T value; private readonly bool hasValue; public Nullable(T value) { this.value = value; hasValue = true; } public T Value { get { if(!hasValue) throw some exception ;-p return value; } } public T GetValueOrDefault() { return value; } public bool HasValue {get {return hasValue;}} public static explicit operator T(Nullable<T> value) { return value.Value; } public static implicit operator Nullable<T>(T value) { return new Nullable<T>(value); } }
Extra differences, though:
- special boxing rules (you can't normally do this)
- special C# rules for comparing to null etc
- "lifted" operators in C# (and in .NET via
EqualityComparer<T>, Comparer<T> etc) - special rules on generic type constraints (to prevent
Nullable<Nullable<T>>)