C# var Type Inference: When to Use It and When to Avoid
c# var type inference: Understand how C# var infers types at compile time, when it improves readability, and when explicit typing is the better choice.
c# var type inference requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
The var keyword in C# lets you declare a local variable without spelling out its type. The compiler infers the type from the initializer expression. This is not a runtime feature; the inferred type is fixed at compile time and stored in the assembly metadata. For example, var count = 5; is identical to int count = 5; after compilation.
What var Actually Does
When you write var, the compiler looks at the right-hand side of the assignment and determines the static type of the expression. The variable then has that exact type for its entire scope. You cannot assign a value of a different type later, because the variable is not a loosely typed container. This is a compile-time substitution; the generated IL is the same as if you had written the explicit type.
var number = 42; // number is int, not dynamic // number = "text"; // Compile error: cannot implicitly convert string to int
The only requirement is that the initializer must have a type. You cannot use var without an initializer, and you cannot use it for fields, properties, or return types in most contexts. It is restricted to local variables where the type can be inferred from the expression.
How Type Inference Works at Compile Time
The compiler performs type inference based on the static type of the initializer. For method calls, the return type is used. For generic methods, the compiler uses the arguments to infer the type parameters. This is especially useful with LINQ, where the result types are often complex and verbose.
var query = customers.Where(c => c.Age > 30).Select(c => c.Name); // The type is IEnumerable<string>, but writing it explicitly would be noisy
Inference is not dynamic dispatch. The variable is strongly typed. If the initializer type is IEnumerable<Customer>, then var gives you exactly that interface type, not a concrete list. This matters when you later call methods that exist only on the concrete type.
When var Improves Readability
var can make code easier to read when the type is obvious from the initializer. For example, var settings = new Dictionary<string, List<int>>(); is clearer than repeating the generic type on both sides. The reader does not need to parse the type twice. This is especially valuable in complex generic expressions where the explicit type adds noise without adding information.
var lookup = new Dictionary<string, List<Order>>(); // vs Dictionary<string, List<Order>> lookup = new Dictionary<string, List<Order>>();
In loops, var is often used with foreach to avoid writing a long type that is already implied by the collection. It also reduces the chance of mismatched types when refactoring, because the variable follows the initializer's type automatically.
When Explicit Types Are Clearer
Explicit typing is better when the type is not obvious from the initializer, especially when the initializer returns an interface, a base class, or a less specific type than what you need. For example, if a method returns IEnumerable<Customer>, using var hides that you are working with an interface. If you later need to call a method specific to List<Customer>, you would have to cast. Writing the explicit type makes the intent clear.
IEnumerable<Customer> customers = GetCustomers(); // explicit var customers = GetCustomers(); // same type, but less visible
When the initializer is a method call with a non-obvious return type, explicit typing acts as documentation. It also prevents accidental type changes if the method's return type changes in a future version. If the return type changes from List<Customer> to IEnumerable<Customer>, a var variable will silently change behavior, potentially breaking downstream calls. An explicit type would force a compile error or require a change.
var with Anonymous Types and LINQ
Anonymous types have no type name you can write in source code, so var is the only way to declare a variable that holds one. This is common in LINQ projections where you select a subset of properties.
var result = from c in customers select new { c.Name, c.Age }; foreach (var item in result) { Console.WriteLine($"{item.Name}: {item.Age}"); }
The compiler generates a unique internal type for each anonymous type. Using var is required because you cannot name that type. This is a legitimate and necessary use of implicit typing.
Common Misconceptions: var Is Not dynamic
A frequent misunderstanding is that var behaves like dynamic or object. It does not. The variable's type is fixed at compile time. There is no runtime binding, no performance penalty, and no loss of IntelliSense. The compiler treats the variable exactly as if you had written the explicit type. This means you get full compile-time checking, refactoring support, and performance characteristics identical to explicit typing.
dynamic d = 42; // runtime type is int, but compile-time type is dynamic d = "hello"; // allowed, but no compile-time checking var v = 42; // compile-time type is int // v = "hello"; // compile error
Using var does not introduce any runtime overhead. The IL is the same as with an explicit type. The only difference is in the source code, not in the compiled output.
Maintainability and Refactoring with var
var can make refactoring easier because you change the initializer and the variable type follows automatically. For example, if you change a method to return a more derived type, all var variables that use it will automatically see the new type. This can be helpful when you are changing implementations and want to minimize edits.
However, this same behavior can hide breaking changes. If a method's return type changes from List<Customer> to IEnumerable<Customer>, a var variable will silently lose access to List-specific methods. An explicit type declaration would cause a compile error, forcing you to address the change. The choice between var and explicit typing is a tradeoff between convenience and explicitness.
A common guideline is to use var when the type is apparent from the initializer and to use explicit types when the type is not obvious or when you want to enforce a specific contract. This keeps the code readable without hiding important information.
Compatibility and Edge Cases
var cannot be used in every situation. It is only allowed for local variables with an initializer. You cannot use it for fields, properties, or method return types (except in certain C# 9 features like target-typed new, but not var). Also, var requires that the initializer have a type; you cannot use it with a null literal because null has no type. For example, var x = null; does not compile.
// var x = null; // compile error: cannot assign null to an implicitly-typed variable
In older C# versions, var was also required for anonymous types, but since C# 7 you can use tuple types with named fields, which are also anonymous in a sense but have a syntax you can write. Still, var remains useful for LINQ projections and for reducing repetition in generic declarations.
When working with nullable reference types, var preserves the nullability annotation from the initializer. If a method returns string?, then var s = GetString(); gives s the type string?. This is consistent with the compiler's inference rules and does not introduce unexpected nullability.
In summary, var is a compile-time feature that infers the type from the initializer. It is not dynamic, it has no runtime cost, and it can improve readability when the type is obvious. The key is to use it where it clarifies and avoid it where it hides important type information. The decision should be based on whether the type is evident from the context and whether the variable's type is part of the intended contract.