C# 7.0 introduced the concept of discards. Discards are intentionally unused, temporarily dummy variables that we don’t care about and don’t want to use.
For example, the following shows the result of an addition being discarded:
_ = 1 + 1;
Note the underscore _ this is the discard character.
Given the preceding example, you cannot access the result of this addition, for example:
WriteLine(_); // Error CS0103 The name '_' does not exist in the current context
Using C# Discards with Out Parameters
A more useful example is when you are working with a method that has one or more out parameters and you don’t care about using the outputted value.
As an example, consider one of the many TryParse methods in .NET such as int.TryParse. The following code show a method that writes to the console whether or not a string can be parsed as an int:
static void ParseInt()
{
WriteLine("Please enter an int to validate");
string @int = ReadLine();
bool isValidInt = int.TryParse(@int, out int parsedInt);
if (isValidInt)
{
WriteLine($"{@int} is a valid int");
}
else
{
WriteLine($"{@int} is NOT a valid int");
}
}
The preceding method can be written using a discard because the out int parsedInt value is never used:
static void ParseIntUsingDiscard()
{
WriteLine("Please enter an int to validate");
string @int = ReadLine();
if (int.TryParse(@int, out _))
{
WriteLine($"{@int} is a valid int");
}
else
{
WriteLine($"{@int} is NOT a valid int");
}
}
For example we could create an expression bodied method using a similar approach:
static bool IsInt(string @int) => int.TryParse(@int, out _);
If you have a method that returns a lot of out values such as:
private static void GenerateDefaultCity(out string name, out string nickName, out long population, out DateTime founded)
{
name = "London";
nickName = "The Big Smoke";
population = 8_000_000;
founded = new DateTime(50, 1, 1);
}
In this case you might only care about the returned population value so you could discard all the other out values:
GenerateDefaultCity(out _,out _, out var population, out _);
WriteLine($"Population is: {population}");
Using C# Discards with Tuples
Another use for discards is where you don’t care about all the fields of a tuple. For example the following method returns a tuple containing a name and age:
static (string name, int age) GenerateDefaultPerson()
{
return ("Amrit", 42);
}
If you only cared about the age you could write:
var (_, age) = GenerateDefaultPerson();
WriteLine($"Default person age is {age}");
Simplifying Null Checking Code with Discards
Take the following null checking code:
private static void Display(string message)
{
if (message is null)
{
throw new ArgumentNullException(nameof(message));
}
WriteLine(message);
}
You could refactor this to make use of throw expressions:
private static void DisplayV2(string message)
{
string checkedMessage = message ?? throw new ArgumentNullException(nameof(message));
WriteLine(checkedMessage);
}
In the preceding version however, the checkedMessage variable is somewhat redundant, this could be refactored to use a discard:
private static void DisplayWithDiscardNullCheck(string message)
{
_ = message ?? throw new ArgumentNullException(nameof(message));
WriteLine(message);
}
Using C# Discards with Tasks
Take the following code:
// Warning CS1998 This async method lacks 'await' operators and will run synchronously.
Task.Run(() => SayHello());
Where the SayHello method is defined as:
private static string SayHello()
{
string greeting = "Hello there!";
return greeting;
}
If we don’t care about the return value and want to discard the result and get rid of the compiler warning::
// With discard - no compiler warning
_ = Task.Run(() => SayHello());
If there are any exceptions however, they will be supressed:
await Task.Run(() => throw new Exception()); // Exception thrown
_ = Task.Run(() => throw new Exception()); // Exception suppressed
Pattern Matching with Switch Statements and Discards
You can also use discards in switch statements:
private static void SwitchExample(object o)
{
switch (o)
{
case null:
WriteLine("o is null");
break;
case string s:
WriteLine($"{s} in uppercase is {s.ToUpperInvariant()}");
break;
case var _:
WriteLine($"{o.GetType()} type not supported.");
break;
}
}
If you want to fill in the gaps in your C# knowledge be sure to check out my C# Tips and Traps training course from Pluralsight – get started with a free trial.
SHARE: