Discard feature significance in C# 7.0?

C#C# 7.0

C# Problem Overview


While going through new C# 7.0 features, I stuck up with discard feature. It says:

> Discards are local variables which you can assign but cannot read > from. i.e. they are “write-only” local variables.

and, then, an example follows:

if (bool.TryParse("TRUE", out bool _))

What is real use case when this will be beneficial? I mean what if I would have defined it in normal way, say:

if (bool.TryParse("TRUE", out bool isOK))

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

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.

Solution 3 - C#

To add another use case to the above answers.

You can use a discard in conjunction with a null coalescing operator to do a nice one-line null check at the start of your functions:

_ = myParam ?? throw new MyException();

Solution 4 - C#

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.)

Solution 5 - C#

Discard pattern can be used with a switch expression as well.

string result = shape switch
{
     Rectangule r => $"Rectangule",
     Circle c => $"Circle",
     _ => "Unknown Shape"
};

For a list of patterns with discards refer to this article: Discards.

Solution 6 - C#

Consider this:

5 + 7;

This "statement" performs an evaluation but is not assigned to something. It will be immediately highlighted with the CS error code CS0201.

// Only assignment, call, increment, decrement, and new object expressions can be used as a statement

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0201?f1url=%3FappId%3Droslyn%26k%3Dk(CS0201)

A discard variable used here will not change the fact that it is an unused expression, rather it will appear to the compiler, to you, and others reviewing your code that it was intentionally unused.

_ = 5 + 7; //acceptable

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionrahulaga-msftView Question on Stackoverflow
Solution 1 - C#Tommaso BelluzzoView Answer on Stackoverflow
Solution 2 - C#ZzeView Answer on Stackoverflow
Solution 3 - C#PersistenceView Answer on Stackoverflow
Solution 4 - C#Loren PechtelView Answer on Stackoverflow
Solution 5 - C#GrishaView Answer on Stackoverflow
Solution 6 - C#Peter ShenView Answer on Stackoverflow