In many development teams, there are conversations or even arguments about whether in C# we should use var
or the explicit type of an object when declaring variables.
In this post, we will look at the implications and my opinion on the matter.
Table of Contents
1- Performance Impact When Using var in C#
None.
Using the var
keyword or the explicit type when defining variables has no effect on performance. This is because var does not mean that the type is dynamic; it simply means that the type is inferred by the compiler at compile-time. The technical term in English is "inferred."
Here we can see an example with code in C# and in IL. We see that both versions are identical:
Therefore, all the implications we might find are visual or relate to the development experience.
2 - Should I Use var or the Explicit Type?
Since there’s no effect on performance, it all comes down to preference. Before continuing, I want to say that I have a preference, but even so, I would never block a code review if someone uses the other approach, because I understand it’s not a real issue. There are companies that define whether to use var or the explicit type, but it’s not that common.
In my case, I use the explicit type almost always and I’ll explain why.
The use of var seems confusing to me because it’s a complement for the variable name; it’s a way of explaining what type it is, or whether it’s an interface, etc.
Many developers justify using var by saying it's easier to refactor. But let’s be honest: when was the last time you refactored a type and had to change it more than a handful of times? That is, almost never.
In fact, at this point, in my opinion, the use of var is a drawback because it hides part of the change. Often, when refactoring a type with var, the code doesn’t break, but the type is actually different. This means the process doesn’t seem to be affected at first glance, but it could still cause a bug. If you have tests and you catch it before production, that’s great, but if not, there’s a good chance something will break in production.
A very simple example is changing a variable from double
to decimal
, or vice versa, where using var could mask the real type and result in a value with less precision than needed. This actually happened to me not long ago, and the bug made it to production.
double thisIsDouble = 12;decimal thisIsisDecimal = 12;thisIsisDecimal = thisIsDouble; //breaks
That code breaks because the type is explicitly specified. But if you use var
it works:
var thisIsDouble = 12;decimal thisIsisDecimal = 12;thisIsisDecimal = thisIsDouble; //wroks
The reason it works is that now var
is no longer double, it’s decimal
. In real-world practice it shouldn’t cause issues, but it could depending on how things work behind the scenes.
In any case, the main reason I use the explicit type is readability. When it comes to the blog or the YouTube channel, it’s obvious – I talk more or less quickly, and the more information you have on the screen, the better.
For example, in this code from one of the tests in my book Complete Guide to FULL STACK DEVELOPMENT with .NET:
namespace FlagX0.UnitTests.Web.Business.UseCases.Flags{ public class AddFlagUseCaseTest { [Fact] public async Task WhenFlagNameAlreadyExist_ThenError() { //arrange IFlagUserDetails flagUserDetails = new FlagUserDetailsStub(); ApplicationDbContext inMemoryDb = GetInMemoryDbContext(flagUserDetails); FlagEntity currentFlag = new FlagEntity() { UserId = flagUserDetails.UserId, Name = "name", Value = true }; inMemoryDb.Flags.Add(currentFlag); await inMemoryDb.SaveChangesAsync(); //Act AddFlagUseCase addFlagUseCase = new AddFlagUseCase(inMemoryDb, flagUserDetails); var result = await addFlagUseCase.Execute(currentFlag.Name, true); //assert Assert.False(result.Success); Assert.Equal("Flag Name Already Exist", result.Errors.First().Message); } [Fact] public async Task WhenFlagDoesNotExist_ThenInsertedOnDb() { //arrange IFlagUserDetails flagUserDetails = new FlagUserDetailsStub(); ApplicationDbContext inMemoryDb = GetInMemoryDbContext(flagUserDetails); //act AddFlagUseCase addFlagUseCase = new AddFlagUseCase(inMemoryDb, flagUserDetails); var result = await addFlagUseCase.Execute("flagName", true); //assert Assert.True(result.Success); Assert.True(result.Value); } private ApplicationDbContext GetInMemoryDbContext(IFlagUserDetails flagUserDetails) { DbContextOptions<ApplicationDbContext> databaseOptions = new DbContextOptionsBuilder<ApplicationDbContext>() .UseInMemoryDatabase("flagx0Db") .Options; return new ApplicationDbContext(databaseOptions, flagUserDetails); } } public class FlagUserDetailsStub : IFlagUserDetails { public string UserId => "1"; }}
You can clearly see the type of each variable. Even with good variable names there can still be doubts, and if we use var
there would be doubts about the type returned by the GetInMemoryDbContext
method. But since we specify the type, we know it’s the normal in-memory DbContext
.
But the same applies in the workplace, where using the explicit type shows the reader what type it is, which greatly facilitates code reviews. Referring back to the previous case, where a user creates a new test, you can see each type. With var, if you were a new person working on that code, you wouldn’t be able to see the type in the code review app, because usually it doesn’t load the whole file.
Another very important example is with values that can be null. Imagine you haven’t seen my post on the importance of good programming and don’t have alerts for warnings enabled.
In this scenario, we return an object that can be null and then access a value on it. If we see the following in a code review, it will probably pass inspection:
public async Task<FlagEntity?> GetByName(string flagname) => await applicationDbContext.Flags .Where(a => a.Name.Equals(flagname, StringComparison.InvariantCultureIgnoreCase)) .AsNoTracking() .FirstOrDefaultAsync();
And we access it like this:
public async Task<Result<FlagDto>> Example(string name){ var entity = await GetByName(name); return entity.ToDto();}
But if we specify the type, in the review we’ll see it’s a nullable object and therefore before accessing any property we need to verify it’s not null, because otherwise it could throw an exception. So we should check the value, which is much easier to spot in a code review.
public async Task<Result<FlagDto>> Example(string name){ var entity = await GetByName(name); if (entity != null) { return entity.ToDto(); } return Result.NotFound<FlagDto>("FlagDoes not exist");}
Most IDEs have functionality to alert you or highlight errors in yellow or red, making it visually obvious when there’s a problem, but that functionality isn’t available when reviewing code in a web interface. Therefore, in my opinion, using the explicit type makes it easier to read code, which leads to better code reviews and understanding, and for me, that’s a strong enough reason.
NOTE: We could discuss whether we should use the coalesce operator to simplify the if; but that's another story.
Finally, when defining variables in the latest versions of .NET we now have another alternative. You can specify the explicit type on the left and leave out the type in the right-hand declaration.
FlagDto testFlagVar = new(){ Name = "example", IsEnabled = true, Id = 1};
This option is becoming more popular and for me, it’s slowly becoming my favorite since it tends to end the debate of using var
or the explicit type.
If there is any problem you can add a comment bellow or contact me in the website's contact form