Chapter 03 Csharp 3 Linq ยท Quick recall Q&A

6 min read
Mid-level4 min read
Rapid overview

Quick recall Q&A

Q: Is a var local dynamically typed?

No. The compiler infers one static type from the initializer at compile time and the variable keeps it; var s = "a"; s = 42; is a compile error. Dynamic binding is dynamic, not var.

Q: When is var required rather than optional?

When the type can't be named: holding an anonymous type, a query that projects anonymous types, and the foreach variable iterating such a query.

Q: Name the rules for declaring a var local.

Local scope only (locals, for, foreach, using, never fields/parameters/returns); declared and initialised in the same statement; not initialised with a bare null; one declarator per statement; the variable can't appear in its own initializer; a lambda or method group only if it has a natural type (C# 10+).

Q: Why can't var be used for fields?

The compiler records every field's type before it analyses any initializer expression, so it can't infer a field's type from its initializer.

Q: What does var s = "hello"; infer in a nullable-enabled context?

string?. With nullable reference types on, var always infers the nullable reference type; flow analysis still tracks that the current value is non-null.

Q: What's the difference between var, object and dynamic?

var is compile-time inference of the real type, with no runtime cost. object is a real static type: you must cast to use members, and value types get boxed. dynamic defers member binding to runtime, so typos compile and throw RuntimeBinderException, with binder overhead and boxing.

Q: Give two cases where var hurts readability.

When the right-hand side hides the type (var result = service.Process(order);), and when the static type decides behaviour, such as IQueryable<T> vs IEnumerable<T> or var total = 0; being int when you meant decimal.

Q: Is implicit typing the same thing as an implicit conversion?

No. Implicit typing (var) is about who writes a variable's type: the compiler infers it at compile time. An implicit conversion is about a value changing type without a cast, like int widening to long. var x = 5; long y = x; uses one of each.

Q: What is target-typed new() and how does it relate to var?

C# 9 syntax where the type comes from the left side: List<int> xs = new();. It's the mirror image of var, where the type comes from the right side. var x = new(); fails because neither side carries a type.

Q: Which numeric conversions are implicit, and can an implicit one lose information?

Widening ones: integral types to a wider type that can hold every value (byteโ†’intโ†’long; not intโ†’uint), any integral type to float/double/decimal, float to double. They never throw, but int/long โ†’ float and long โ†’ double can lose precision (not magnitude).

Q: What does (byte)300 evaluate to, and how do you make it throw instead?

44 in the default unchecked context (the high bits are discarded). Wrap it in checked((byte)value) or enable <CheckForOverflowUnderflow> to get an OverflowException. As a constant, (byte)300 needs unchecked(...) to compile at all.

Q: When should a user-defined conversion be implicit rather than explicit?

Only when it can never throw and never loses information, matching the built-in implicit conversions. Anything that can fail or truncate should be explicit, so the caller has to write a cast.

Q: Why does decimal price = 9.99; fail to compile?

9.99 is a double literal and there is no implicit conversion from double to decimal. Use the m suffix: 9.99m.

Q: How is dynamic different from var?

var infers a real static type at compile time and the compiler checks every use. dynamic is a static type that defers member binding, overload resolution and conversions to runtime, so errors surface as RuntimeBinderException.

Q: Give three situations where an explicit type beats var.

When the initializer doesn't show the type (var x = repo.Load(id);); when you want a different type than the initializer's, such as an interface, a wider number or a nullable (IReadOnlyList<T> items = new List<T>();, long total = 0;); and when the static type changes behaviour, like IQueryable<T> vs IEnumerable<T>.

Q: What does the compiler generate for new { Name = "Ada", Age = 36 }?

An internal sealed class deriving from object, with public read-only properties, a constructor, and overrides of Equals, GetHashCode and ToString that use the property values.

Q: Are anonymous types mutable?

No. Every property is read-only; to "change" one you create a new instance, e.g. with a with expression (C# 10+). The immutability is shallow: a referenced list can still be modified.

Q: For two anonymous objects with equal values, what do a.Equals(b) and a == b return?

a.Equals(b) is true (generated value equality). a == b is false, because anonymous types are classes without an == overload, so == compares references.

Q: When do two anonymous object expressions have the same type?

When they're in the same assembly and declare the same property names with the same types in the same order. new { A = 1, B = 2 } and new { B = 2, A = 1 } are different types.

Q: Why can't you return an anonymous type from a method?

You can't write its name, so it can't be a return, parameter or field type. Returning it as object loses the static type; use a record or tuple instead.

Q: Why are anonymous types still used in EF Core projections when tuples exist?

Tuple literals aren't allowed in expression trees, and anonymous types are, so Select(o => new { o.Id, o.Total }) translates into a SQL SELECT of just those columns.

Q: How do anonymous types differ from value tuples?

Anonymous types are read-only classes whose == compares references; value tuples are mutable structs whose == compares elements and whose names exist only at compile time. Tuples can cross method boundaries and be deconstructed; anonymous types can't, but they work in expression trees.

Q: When should an anonymous type become a record?

As soon as the shape leaves the method: returned, stored in a field or cache, serialized, or sent as a message. A record keeps the value equality and with support and gives you a nameable, versionable contract.

See also