Equality, Nullability & Dynamic
Value versus reference equality, Equals/GetHashCode, nullable types, immutability, var/dynamic/object, and type inference.
5 questions
MiddleTheoryVery commonWhat is Nullable<T> / int?, and how do you read its value safely?
What is Nullable<T> / int?, and how do you read its value safely?
Nullable<T> (shorthand int?) is a struct letting a value type also represent null. It exposes HasValue to test presence and Value to read it, which throws when empty. Safe reads use GetValueOrDefault() or ?? for a fallback.
Common mistakes
- ✗Reading
.Valuewithout checkingHasValue, which throwsInvalidOperationExceptionwhen empty - ✗Thinking
int?is a reference type rather than astructwrapping the value type - ✗Assuming
Nullable<T>works for reference types, whenTmust be a non-nullable value type
Follow-up questions
- →What does
GetValueOrDefault()return when the nullable has no value? - →How does lifting let arithmetic on two
int?operands produce anullresult?
JuniorTheoryCommonWhat does var do, and is the variable it declares dynamically typed?
What does var do, and is the variable it declares dynamically typed?
var tells the compiler to infer the variable's STATIC type from its initializer at compile time. It is exactly equivalent to the explicit type — var x = 5; is int x = 5;. The variable is statically typed and fixed; var is not dynamic.
Common mistakes
- ✗Confusing
varwithdynamicand assuming it bypasses compile-time type checking - ✗Believing a
varvariable can be reassigned to a different type after declaration - ✗Thinking
varhas a runtime performance cost versus naming the type explicitly
Follow-up questions
- →Why must a
vardeclaration always have an initializer on the same line? - →When is
varrequired rather than optional in C# code?
MiddleTheoryCommonHow do == and Equals() differ for classes versus value types?
How do == and Equals() differ for classes versus value types?
For a class, both == and Equals() default to reference equality; value types default to value equality. == is a static, overloadable operator resolved at compile time; Equals is an overridable virtual method. string overrides both to compare values.
Common mistakes
- ✗Expecting
==on two distinct class instances with equal fields to return true by default - ✗Thinking
==is virtual and dispatches likeEquals, when it is a statically resolved operator - ✗Assuming
string ==compares references rather than the character sequence by value
Follow-up questions
- →Why should overriding
Equalsalso mean overridingGetHashCode? - →How can
==andEqualsgive different results on the same two operands?
MiddleTheoryOccasionalWhat does it mean that string is immutable, and why does it matter?
What does it mean that string is immutable, and why does it matter?
Immutable means an object's state cannot change after construction. Because string is immutable, every apparent mutation like Replace or ToUpper allocates a new string and leaves the original intact. For heavy in-loop edits use StringBuilder.
Common mistakes
- ✗Believing a string method like
Replacemodifies the original instead of returning a newstring - ✗Concatenating in a loop with
+, creating many short-lived strings instead of usingStringBuilder - ✗Confusing immutability of the string object with whether the variable holding it can be reassigned
Follow-up questions
- →How does string interning exploit immutability to share identical literals?
- →Why is an immutable type inherently safe to read from multiple threads without locking?
MiddleTheoryOccasionalHow do var, dynamic, and object differ in when types are resolved?
How do var, dynamic, and object differ in when types are resolved?
var is compile-time inference: the variable gets a concrete static type with full checking. object is the static base type of everything, so you must cast to call anything specific. dynamic defers member binding to runtime; an invalid member throws at runtime.
Common mistakes
- ✗Confusing
var(compile-time static type) withdynamic(runtime binding) - ✗Thinking
objectskips compile-time member checks, when it requires an explicit cast - ✗Assuming
dynamicerrors are caught at compile time rather than thrown at runtime
Follow-up questions
- →What runtime infrastructure does
dynamicuse to resolve member calls? - →Why does calling a method on an
objectvariable require a cast first?