Discard feature significance in C# 7.0?

The discards are basically a way to intentionally ignore local variables which are irrelevant for the purposes of the code being produced. It's like when you call a method that returns a value but, since you are interested only in the underlying operations it performs, you don't assign its output to a local variable defined in the caller method, for example:

public static void Main(string[] args)
{
    // I want to modify the records but I'm not interested
    // in knowing how many of them have been modified.
    ModifyRecords();
}

public static Int32 ModifyRecords()
{
    Int32 affectedRecords = 0;

    for (Int32 i = 0; i < s_Records.Count; ++i)
    {
        Record r = s_Records[i];

        if (String.IsNullOrWhiteSpace(r.Name))
        {
            r.Name = "Default Name";
            ++affectedRecords;
        }
    }

    return affectedRecords;
}

Actually, I would call it a cosmetic feature... in the sense that it's a design time feature (the computations concerning the discarded variables are performed anyway) that helps keeping the code clear, readable and easy to maintain.

I find the example shown in the link you provided kinda misleading. If I try to parse a String as a Boolean, chances are I want to use the parsed value somewhere in my code. Otherwise I would just try to see if the String corresponds to the text representation of a Boolean (a regular expression, for example... even a simple if statement could do the job if casing is properly handled). I'm far from saying that this never happens or that it's a bad practice, I'm just saying it's not the most common coding pattern you may need to produce.

The example provided in this article, on the opposite, really shows the full potential of this feature:

public static void Main()
{
    var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
    Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
}

private 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);
}

From what I can see reading the above code, it seems that the discards have a higher sinergy with other paradigms introduced in the most recent versions of C# like tuples deconstruction.


For Matlab programmers, discards are far from being a new concept because the programming language implements them since very, very, very long time (probably since the beginning, but I can't say for sure). The official documentation describes them as follows (link here):

Request all three possible outputs from the fileparts function:

helpFile = which('help');
[helpPath,name,ext] = fileparts('C:\Path\data.txt');

The current workspace now contains three variables from fileparts: helpPath, name, and ext. In this case, the variables are small. However, some functions return results that use much more memory. If you do not need those variables, they waste space on your system.

Ignore the first output using a tilde (~):

[~,name,ext] = fileparts(helpFile);

The only difference is that, in Matlab, inner computations for discarded outputs are normally skipped because output arguments are flexible and you can know how many and which one of them have been requested by the caller.


I have seen discards used mainly against methods which return Task<T> but you don't want to await the output.

So in the example below, we don't want to await the output of SomeOtherMethod() so we could do something like this:

//myClass.cs
public async Task<bool> Example() => await SomeOtherMethod()

// example.cs
Example();

Except this will generate the following warning:

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.

To mitigate this warning and essentially ensure the compiler that we know what we are doing, you can use a discard:

//myClass.cs
public async Task<bool> Example() => await SomeOtherMethod()

// example.cs
_ = Example();

No more warnings.


Many times I've done code along these lines:

TextBox.BackColor = int32.TryParse(TextBox.Text, out int32 _) ? Color.LightGreen : Color.Pink;

Note that this would be part of a larger collection of data, not a standalone thing. The idea is to provide immediate feedback on the validity of each field of the data they are entering.

I use light green and pink rather than the green and red one would expect--the latter colors are dark enough that the text becomes a bit hard to read and the meaning of the lighter versions is still totally obvious.

(In some cases I also have a Color.Yellow to flag something which is not valid but neither is it totally invalid. Say the parser will accept fractions and the field currently contains "2 1". That could be part of "2 1/2" so it's not garbage, but neither is it valid.)

Tags:

C#

C# 7.0