Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Discard feature significance in C# 7.0?

Tags:

c#

c#-7.0

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))
like image 260
rahulaga_dev Avatar asked Feb 25 '18 04:02

rahulaga_dev


People also ask

What is discard _?

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.

Why do we use _ in C#?

The underscore before a variable name _val is nothing more than a convention. In C#, it is used when defining the private member variable for a public property.


5 Answers

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.

like image 71
Tommaso Belluzzo Avatar answered Oct 22 '22 03:10

Tommaso Belluzzo


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.

like image 31
Zze Avatar answered Oct 22 '22 03:10

Zze


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();
like image 15
Persistence Avatar answered Oct 22 '22 05:10

Persistence


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

like image 9
Loren Pechtel Avatar answered Oct 22 '22 04:10

Loren Pechtel


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.

like image 2
Grisha Avatar answered Oct 22 '22 05:10

Grisha