In many development teams, there are ongoing conversations or even arguments about whether in C# we should use var
or specify the type of an object when declaring variables.
In this post, we'll look at the implications and my opinion on the matter.
1- Performance Impact of 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 the type is dynamic. Instead, at compile time, it uses the inferred type. The technical word in English is "inferred."
Here we can see an example with the C# code and IL code. We see that both versions are the same:
Therefore, any implications are visual or relevant only during development.
2 - Should I Use var or the Explicit Type?
Since there is no impact on performance, it all comes down to preference. Before going on, I want to clarify that I have a preference, and despite that, I would never block a code review if someone used the other option because I understand it's not a real problem. There are companies that define whether to use var or explicit type, but that's not usually the norm.
Personally, I almost always use the explicit type, and I will explain why.
The use of var seems confusing to me because it's just an addition to the variable name; it's a way to signify what type it is, or if it's an interface, etc.
Many developers justify using var by saying it makes refactoring easier. But, let's be honest, when was the last time you actually needed to refactor, and not just that, needed to change a type more than a handful of times? Exactly, almost never.
In fact, at this point, in my opinion using var can be harmful, because it hides part of the change. Often, when refactoring a type with var, the code doesn't break but the underlying type is different, which means the process happening at that moment may not be obviously affected, but a bug may still occur. If we have tests and catch this before reaching production, that'd be great, but if not, chances are high that something could break in production.
A very simple example is changing a variable from double
to decimal
, or vice versa, where the use of var could mask the real type and lead to having less precision than needed. This actually happened to me a few months ago, and the bug made it to production.
double thisIsDouble = 12;
decimal thisIsisDecimal = 12;
thisIsisDecimal = thisIsDouble; //breaks
That code breaks, because you have the specific type. 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 a double, but a decimal
. In real life, it shouldn’t cause problems, but depending on how things work under the hood, it could.
Regardless, my main reason for using the explicit type is readability. On the blog or the YouTube channel, it’s clear, 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 inside my book The 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 easily see the type of each variable, and even with good variable names, there can always be doubts. If we use var
, there could be confusion about what type is returned by the GetInMemoryDbContext
method, but since the type is specified, we know it’s the normal in-memory DbContext
.
The same applies in a work environment, where using the explicit type tells the reader what the type is, which makes code reviews much easier. Going back to the previous example, where a user creates a new test, you can see every type. With var, if you are new to the codebase, you won’t necessarily know what type is, because normally the code review app doesn’t load the entire file.
Another very important example is with values that can be null. Imagine you haven’t read my post about the importance of programming well and you don’t have alerts for warnings enabled.
In this scenario, we return an object that could be null, and then access a value on that object. If we see the following in a code review, it would likely be accepted without question:
public async Task<FlagEntity?> GetByName(string flagname)
=> await applicationDbContext.Flags
.Where(a => a.Name.Equals(flagname, StringComparison.InvariantCultureIgnoreCase))
.AsNoTracking()
.FirstOrDefaultAsync();
And we access it as follows:
public async Task<Result<FlagDto>> Example(string name)
{
var entity = await GetByName(name);
return entity.ToDto();
}
But if we specify the type, we’ll see in the review that it’s a nullable object, and therefore before accessing any of its properties, you need to verify that it's not null, because otherwise an exception could occur. So we must check, 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");
}
The vast majority of IDEs will show a warning or highlight in yellow/red to let you know there’s an issue, but that functionality isn't available when reviewing code in a web interface. Therefore, in my opinion, using the explicit type makes the code easier to read, which in turn makes code reviews more effective and understandable—which is reason enough for me.
NOTE: We could start discussing whether we should simplify the if with the coalesce operator, but that’s another story.
Finally, when defining variables in the latest versions of .NET, there is a new alternative where you can specify the explicit type on the left and omit the type on the right-hand side of the declaration.
FlagDto testFlagVar = new()
{
Name = "example",
IsEnabled = true,
Id = 1
};
This option is becoming more common, and for me, it's slowly becoming my favorite since it often ends the debate between 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