Last Updated: May 22, 2026
C# is statically typed, but you don't always have to write the type out by hand. The var keyword lets the compiler figure out the type of a local variable from the value you assign to it. The variable is still strongly typed, the compiler just saves you the keystrokes. This lesson covers what var actually does, the rules around when you can and can't use it, and the cases where it makes code clearer versus the cases where it hides important information.
var Actually MeansWhen you write var name = "Alice";, the compiler looks at the right side, sees a string literal, and types the variable as string. The result is identical to string name = "Alice"; in every way the runtime cares about. The compiled IL contains a string variable, not a var variable. There is no var type.
Both variables are string. The var keyword is a compile-time convenience called type inference. It's not a special kind of variable, and it's not the same as the var keyword in JavaScript (which is dynamically typed) or the dynamic type in C# (which defers type checking to runtime).
This distinction matters because the misunderstanding is common. A reader coming from JavaScript or Python might assume var in C# means "the type can change later." It cannot. Once the compiler infers a type, that type is fixed for the lifetime of the variable.
The second line fails to compile with CS0029: Cannot implicitly convert type 'string' to 'int'. The compiler inferred stockCount as int on line 1, and from that point on, the variable is an int. Reassigning it to a string is the same kind of error you'd get with int stockCount = 10; stockCount = "out of stock";.
The work var does happens entirely during compilation. By the time your program runs, there is no var anywhere in the compiled assembly. The diagram below shows what happens when the compiler sees var price = 19.99m;.
The compiler looks at the literal 19.99m. The m suffix marks it as decimal, so price is inferred as decimal. From that point on, the variable behaves exactly as if you'd written decimal price = 19.99m;. There is no runtime cost. There is no boxing. There is no dynamic dispatch.
That's why var is sometimes called implicit typing: the type is still there, you just didn't type it out.
Compare that with dynamic, which is a separate C# type that defers all member resolution to runtime:
dynamic is a runtime feature, with real runtime cost. var is a compile-time shortcut, with zero runtime cost. The two look similar at the syntax level and behave completely differently. If you remember nothing else, remember that one.
var has a small set of rules the compiler enforces. They all come from the same principle: the compiler must be able to infer a single, unambiguous type from the right-hand side at compile time.
The compiler infers the type from the initializer. No initializer, no inference.
This fails with CS0818: Implicitly-typed variables must be initialized. The compiler can't look ahead to line 2 to guess what type quantity should be. The fix is to combine declaration and assignment:
nullnull doesn't have a type on its own, it's a value that's compatible with many types. The compiler can't pick one.
This fails with CS0815: Cannot assign <null> to an implicitly-typed variable. You have to tell the compiler which type you mean:
You can use var with a typed expression that happens to evaluate to null, though. var name = (string?)null; works because the cast pins down the type.
Once the compiler picks a type, you can't reassign the variable to a value of a different, incompatible type.
Fails with CS0029. orderId is int, and "ORD-1001" is a string. Static typing still applies, you just didn't get to spell the type out.
var works for local variables inside a method, for foreach loop variables, for for loop counters, and in using declarations. It does not work for:
public var Name;. Fields need an explicit type.void Add(var item). Parameters need an explicit type. The one exception, added in C# 10, is lambda parameters, where the compiler can infer types from the delegate's signature.public var GetName(). Return types need an explicit type.Both lines fail. Fields and return types must be declared explicitly. The fix:
The reasoning is partly historical and partly about readability. A class's public surface (its fields, properties, methods) is part of its contract with the outside world. Letting the compiler hide those types behind var would make that contract harder to read and harder to keep stable across versions.
var HelpsThe strongest case for var is when the type is long, obvious, or both. Repeating it on both sides adds noise without adding information.
Constructor calls where the type is right there:
The new on the right tells you exactly what you're getting. Writing List<Product> cart = new List<Product>(); is more characters for the same information.
LINQ projections, where the result is often an anonymous type or a generic interface:
The full type of groupedByCustomer is IEnumerable<IGrouping<int, Order>>. The full type of pendingOrderTotals is IEnumerable<<anonymous type>>. Spelling those out adds clutter without helping the reader.
`foreach` loops over a typed collection:
The collection's element type is Product, so the loop variable is Product. The reader can see that from the collection's declaration two lines up.
Long generic types:
Versus:
The var version says the same thing with less repetition. Modern C# also has target-typed `new` (covered at the end of this lesson) for the same problem from the other direction.
var Hurtsvar is a tool, not a default. Use an explicit type when reading the code without an IDE wouldn't tell you what's going on.
Numeric literals where the exact type matters:
What is the type of price? In C#, a decimal literal with no suffix is a double, so price is double. If you wanted decimal for money (which you do), this is a bug waiting to happen. The explicit form makes the intent unmistakable:
The same problem applies to integer literals and long, float, and decimal suffixes. When the literal's type isn't visually obvious, prefer the explicit declaration.
Return values where you have to look up the method's signature:
What's result? A bool? An Order? A Task<OrderResult>? Unless the reader knows what ProcessOrder returns, the line is opaque. With an explicit type, the question doesn't come up:
Casts and conversions where the conversion is the point:
works, but the var adds nothing. The cast already names the type. Either form is fine, but the explicit version reads more naturally:
The Microsoft style guidance, reflected in the default Roslyn analyzers shipped with .NET, lands on a simple rule: use var when the type is apparent from the right side, and use an explicit type when it isn't. "Apparent" is judgment, but constructor calls, casts, and literal initializers with clear suffixes all qualify. Method return values usually don't.
var vs Explicit| Situation | Use var? | Reason |
|---|---|---|
var cart = new List<Product>(); | Yes | Type is right there in the new. |
var price = 19.99; | No | Reader can't tell if this is double, decimal, or float without thinking. |
var customers = repository.GetAll(); | No | Method return type is invisible at the call site. |
var orderDate = DateTime.Now; | Yes | DateTime.Now is a well-known property returning DateTime. |
foreach (var item in cart.Items) | Yes | Element type is visible from the collection. |
var grouped = orders.GroupBy(o => o.Status); | Yes | The full LINQ type is long and unhelpful to spell out. |
var couponCode = null; | No | Won't compile. |
var lookup = new Dictionary<string, List<Order>>(); | Yes | Avoids repeating the long generic on both sides. |
var total = (decimal)items.Sum(i => i.Price); | Optional | Cast names the type. Either form is fine. |
var customer = await GetCustomerAsync(id); | Use judgment | Customer from the method name is plausible, but Customer? or a wrapper type isn't visible. Explicit is safer. |
The pattern: prefer var when the type is obvious from the right side of the assignment, prefer the explicit type when it isn't, and never use var when it would compile to a type the reader wouldn't guess.
var and Anonymous TypesThere's one place where var isn't just a style choice, it's a requirement. Anonymous types are types the compiler generates on the fly when you use the new { ... } syntax without a class name. They have no name you can write down, so var is the only way to declare a variable of one.
The compiler generates a class with three properties (Name, Price, InStock) and gives it an internal, unspellable name. From your code's point of view, the only handle on that type is the var variable. If you tried to write the type explicitly, you wouldn't be able to, because the type's actual name is something like <>f__AnonymousType0.
Anonymous types come up most often inside LINQ queries, where you're projecting a few properties out of a larger object:
The Select projects each Order into a smaller object with just three properties. There's no named class for that shape, so var is the only declaration that works. This pattern is so common in LINQ code that var appears almost everywhere LINQ shows up.
new (C# 9)var infers the type from right to left: the compiler looks at the value and gives the variable that type. Target-typed new, added in C# 9, does the opposite. It infers the constructor's type from left to right: you write the type once on the left, and the new expression on the right reuses it.
The new() with no type after it tells the compiler "use the type from the left side." It's a different angle on the same readability problem var solves: when you'd otherwise have to write the same long type twice, target-typed new lets you write it once on the left, and var lets you write it once on the right.
The two features overlap, and you can pick whichever reads better for a given case. Most teams pick one as a default and use the other when it fits. The advantage of target-typed new is that the variable's type stays visible in its declaration, which can help in fields and method signatures where var isn't allowed at all:
That field declaration is valid. private readonly var _ordersByCustomer = new Dictionary<int, List<Order>>(); is not, because var doesn't work on fields. Target-typed new works in places var can't.