Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft EdgeMore info about Internet Explorer and Microsoft Edge
Table of contentsExit focus mode

Discards - C# Fundamentals

  • 2025-02-19
Feedback

In this article

Discards are placeholder variables that are intentionally unused in application code. Discards are equivalent to unassigned variables; they don't have a value. A discard communicates intent to the compiler and others that read your code: You intended to ignore the result of an expression. You may want to ignore the result of an expression, one or more members of a tuple expression, anout parameter to a method, or the target of a pattern matching expression.

Discards make the intent of your code clear. A discard indicates that our code never uses the variable. They enhance its readability and maintainability.

You indicate that a variable is a discard by assigning it the underscore (_) as its name. For example, the following method call returns a tuple in which the first and second values are discards.area is a previously declared variable set to the third component returned byGetCityInformation:

(_, _, area) = city.GetCityInformation(cityName);

You can use discards to specify unused input parameters of a lambda expression. For more information, see theInput parameters of a lambda expression section of theLambda expressions article.

When_ is a valid discard, attempting to retrieve its value or use it in an assignment operation generates compiler error CS0103, "The name '_' doesn't exist in the current context". This error is because_ isn't assigned a value, and may not even be assigned a storage location. If it were an actual variable, you couldn't discard more than one value, as the previous example did.

Tuple and object deconstruction

Discards are useful in working with tuples when your application code uses some tuple elements but ignores others. For example, the followingQueryCityDataForYears method returns a tuple with the name of a city, its area, a year, the city's population for that year, a second year, and the city's population for that second year. The example shows the change in population between those two years. Of the data available from the tuple, we're unconcerned with the city area, and we know the city name and the two dates at design-time. As a result, we're only interested in the two population values stored in the tuple, and can handle its remaining values as discards.

var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2){    int population1 = 0, population2 = 0;    double area = 0;    if (name == "New York City")    {        area = 468.48;        if (year1 == 1960)        {            population1 = 7781984;        }        if (year2 == 2010)        {            population2 = 8175133;        }        return (name, area, year1, population1, year2, population2);    }    return ("", 0, 0, 0, 0, 0);}// The example displays the following output://      Population change, 1960 to 2010: 393,149

For more information on deconstructing tuples with discards, seeDeconstructing tuples and other types.

TheDeconstruct method of a class, structure, or interface also allows you to retrieve and deconstruct a specific set of data from an object. You can use discards when you're interested in working with only a subset of the deconstructed values. The following example deconstructs aPerson object into four strings (the first and last names, the city, and the state), but discards the last name and the state.

using System;namespace Discards{    public class Person    {        public string FirstName { get; set; }        public string MiddleName { get; set; }        public string LastName { get; set; }        public string City { get; set; }        public string State { get; set; }        public Person(string fname, string mname, string lname,                      string cityName, string stateName)        {            FirstName = fname;            MiddleName = mname;            LastName = lname;            City = cityName;            State = stateName;        }        // Return the first and last name.        public void Deconstruct(out string fname, out string lname)        {            fname = FirstName;            lname = LastName;        }        public void Deconstruct(out string fname, out string mname, out string lname)        {            fname = FirstName;            mname = MiddleName;            lname = LastName;        }        public void Deconstruct(out string fname, out string lname,                                out string city, out string state)        {            fname = FirstName;            lname = LastName;            city = City;            state = State;        }    }    class Example    {        public static void Main()        {            var p = new Person("John", "Quincy", "Adams", "Boston", "MA");            // Deconstruct the person object.            var (fName, _, city, _) = p;            Console.WriteLine($"Hello {fName} of {city}!");            // The example displays the following output:            //      Hello John of Boston!        }    }}

For more information on deconstructing user-defined types with discards, seeDeconstructing tuples and other types.

Pattern matching withswitch

Thediscard pattern can be used in pattern matching with theswitch expression. Every expression, includingnull, always matches the discard pattern.

The following example defines aProvidesFormatInfo method that uses aswitch expression to determine whether an object provides anIFormatProvider implementation and tests whether the object isnull. It also uses the discard pattern to handle non-null objects of any other type.

object?[] objects = [CultureInfo.CurrentCulture,                   CultureInfo.CurrentCulture.DateTimeFormat,                   CultureInfo.CurrentCulture.NumberFormat,                   new ArgumentException(), null];foreach (var obj in objects)    ProvidesFormatInfo(obj);static void ProvidesFormatInfo(object? obj) =>    Console.WriteLine(obj switch    {        IFormatProvider fmt => $"{fmt.GetType()} object",        null => "A null object reference: Its use could result in a NullReferenceException",        _ => "Some object type without format information"    });// The example displays the following output://    System.Globalization.CultureInfo object//    System.Globalization.DateTimeFormatInfo object//    System.Globalization.NumberFormatInfo object//    Some object type without format information//    A null object reference: Its use could result in a NullReferenceException

Calls to methods without parameters

When calling theDeconstruct method to deconstruct a user-defined type (an instance of a class, structure, or interface), you can discard the values of individualout arguments. But you can also discard the value ofout arguments when calling any method with anout parameter.

The following example calls theDateTime.TryParse(String, out DateTime) method to determine whether the string representation of a date is valid in the current culture. Because the example is concerned only with validating the date string and not with parsing it to extract the date, theout argument to the method is a discard.

string[] dateStrings = ["05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",                      "2018-05-01T14:57:32.8375298-04:00", "5/01/2018",                      "5/01/2018 14:57:32.80 -07:00",                      "1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM",                      "Fri, 15 May 2018 20:10:57 GMT"];foreach (string dateString in dateStrings){    if (DateTime.TryParse(dateString, out _))        Console.WriteLine($"'{dateString}': valid");    else        Console.WriteLine($"'{dateString}': invalid");}// The example displays output like the following://       '05/01/2018 14:57:32.8': valid//       '2018-05-01 14:57:32.8': valid//       '2018-05-01T14:57:32.8375298-04:00': valid//       '5/01/2018': valid//       '5/01/2018 14:57:32.80 -07:00': valid//       '1 May 2018 2:57:32.8 PM': valid//       '16-05-2018 1:00:32 PM': invalid//       'Fri, 15 May 2018 20:10:57 GMT': invalid

A standalone discard

You can use a standalone discard to indicate any variable that you choose to ignore. One typical use is to use an assignment to ensure that an argument isn't null. The following code uses a discard to force an assignment. The right side of the assignment uses thenull coalescing operator to throw anSystem.ArgumentNullException when the argument isnull. The code doesn't need the result of the assignment, so it's discarded. The expression forces a null check. The discard clarifies your intent: the result of the assignment isn't needed or used.

public static void Method(string arg){    _ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");    // Do work with arg.}

The following example uses a standalone discard to ignore theTask object returned by an asynchronous operation. Assigning the task has the effect of suppressing the exception that the operation throws as it is about to complete. It makes your intent clear: You want to discard theTask, and ignore any errors generated from that asynchronous operation.

private static async Task ExecuteAsyncMethods(){    Console.WriteLine("About to launch a task...");    _ = Task.Run(() =>    {        var iterations = 0;        for (int ctr = 0; ctr < int.MaxValue; ctr++)            iterations++;        Console.WriteLine("Completed looping operation...");        throw new InvalidOperationException();    });    await Task.Delay(5000);    Console.WriteLine("Exiting after 5 second delay");}// The example displays output like the following://       About to launch a task...//       Completed looping operation...//       Exiting after 5 second delay

Without assigning the task to a discard, the following code generates a compiler warning:

private static async Task ExecuteAsyncMethods(){    Console.WriteLine("About to launch a task...");    // CS4014: Because this call is not awaited, execution of the current method continues before the call is completed.    // Consider applying the 'await' operator to the result of the call.    Task.Run(() =>    {        var iterations = 0;        for (int ctr = 0; ctr < int.MaxValue; ctr++)            iterations++;        Console.WriteLine("Completed looping operation...");        throw new InvalidOperationException();    });    await Task.Delay(5000);    Console.WriteLine("Exiting after 5 second delay");

Note

If you run either of the preceding two samples using a debugger, the debugger will stop the program when the exception is thrown. Without a debugger attached, the exception is silently ignored in both cases.

_ is also a valid identifier. When used outside of a supported context,_ is treated not as a discard but as a valid variable. If an identifier named_ is already in scope, the use of_ as a standalone discard can result in:

  • Accidental modification of the value of the in-scope_ variable by assigning it the value of the intended discard. For example:
    private static void ShowValue(int _){   byte[] arr = [0, 0, 1, 2];   _ = BitConverter.ToInt32(arr, 0);   Console.WriteLine(_);} // The example displays the following output: //       33619968
  • A compiler error for violating type safety. For example:
    private static bool RoundTrips(int _){   string value = _.ToString();   int newValue = 0;   _ = Int32.TryParse(value, out newValue);   return _ == newValue;}// The example displays the following compiler error://      error CS0029: Cannot implicitly convert type 'bool' to 'int'

See also

Collaborate with us on GitHub
The source for this content can be found on GitHub, where you can also create and review issues and pull requests. For more information, seeour contributor guide.

Feedback

Was this page helpful?

YesNo

In this article

Was this page helpful?

YesNo