In this article, we are going to learn about the differences between a discard variable and the usual variables in C#. We will also see some practical uses of a discard.Â
C# 7 introduced discards as placeholders for values we do not have any use for in our code. They are write-only variables that allow us to explicitly inform the compiler or anyone reading our code to discard their content. They are particularly useful in scenarios where we receive a value but have no intention of using it.
How a Discard Variable Differs From a Usual Variable
A discard is represented by the underscore (_
) character:
_ = "This is the syntax for a discard";
Discards are like unassigned variables. But unlike normal variables, we cannot declare discards with var
or any type keyword.
We can have more than one discard within a scope. We use each underscore placeholder to discard a single value. Even though it looks like discards have values, when we attempt to retrieve any of them, we get a compiler error: CS0103: The name '_' doesn't exist in the current context
.Â
We should note that declaring an underscore with a var
or other types makes it a regular variable and loses its discard functionality:
string _ = "This is no longer a discard";
Why Do We Use a Discard Variable?
If we ignore the discards, why do we need them?
Discards help us maintain a clean codebase with minimized warnings. When we call a method with a return value we do not care for, assigning that value to a discard takes care of compiler warnings about unused variables. Some APIs and libraries also return unwanted values. We employ discards to manage them and still conform to the API/library signatures.
When working with pattern matching and deconstruction, discards allow us to ignore parts of a data structure that are irrelevant to us.Â
They are a feature that contributes to simplifying our codes and improving clarity.
Use Cases for Using a Discard Variable in C#
Using a discard in our code effectively communicates our intention to the compiler and other code readers. It signifies that we deliberately intend to disregard the outcome of an expression.
We could ignore the result of an expression, one or more elements within a tuple expression, an out
parameter in a method or the target of a pattern-matching expression.
Let’s explore various scenarios where discards come in handy.
Discards as Out Parameters
We can discard the value of an out
parameter when calling any method that uses one.Â
For example, a TryParse()
returns a bool
. If the parse is successful, it returns true and saves the value of the parsed argument to the out
parameter:
public static (bool result, string errorMessage) GetNumber(string input) { bool result = int.TryParse(input, out _); if (!result) { return (false, "Not a valid number"); } return (true, ""); }
In our example, we use int.TryParse()
to attempt to parse an input, which is a string
by default. We use discard for the out
parameter to indicate that we are not interested in the parsed integer value. We are only checking if the parse was successful or not.Â
Discards in Tuple Deconstruction
When working with tuples, we often do not require all the values they contain. So, let’s start with a simple method:
public static (int, int, int) GetSum(int num1, int num2) { return (num1, num2, num1 + num2); }
The GetSum()
method returns a three-tuple containing the numbers to be added and the resulting sum.Â
Using a discard, we can selectively ignore the specific tuple elements we want and use actual identifiers for the values we care about:
var (_, _, sum) = DiscardExamples.GetSum(9, 89);
Since the two numbers are already passed into GetSum()
when we call it, we do not need their values returned. Our interest is obtaining the sum, so we designate the other two numbers as discards.
Discards in Pattern Matching
Given an array of items containing various data types, we can use pattern matching within a switch
expression to identify the type of each item:
public static void GetType(object[] objects) { foreach (var item in objects) { Console.WriteLine(item switch { string => "it's a string", int => "it's an int", _ => "Neither string nor int" }); } }
Our method checks if the items in our array are a string
or an integer
. We treat any other data type as a discard, so the method will print Neither string nor int
 to the console.
Discards in Lambda Expressions
From C# 9, we can use discards to represent two or more unused input parameters of a lambda expression:
Func<char, char, char> constant = (_, _) => 'C';
To maintain compatibility for lambda expressions written in older versions of C#, when a single input parameter is assigned the _
identifier, it is interpreted as a valid read/write variable in newer versions.
Conclusion
Using the discard _
in our code conveys to the compiler that we mean to disregard the outcome of an expression deliberately. It proves especially handy in situations where we receive a value but have no plans to utilize it.