I\'ve just come across discards in c# and am wondering a couple things that I feel Microsoft Docs on Discards didn\'t explain very well.
I believe this deals more with initial memory allocation rather than JUST memory management. So if you do not consume the output variable then it is still going to pushed onto the stack regardless if it is a ref type. (Yeah so the val types in your question won't be and I see your point there.) but if you are dealing with something like a filestream and didn't close the stream then I believe you are going to be holding onto that for the application lifecycle. That said then you have to be conscious of anything implementing IDisposable. I would think that discards could have a relatively bigger impact in that regard.
Past that, dealing with out params passed into functions, it prevents the initial memory allocation of those variables since you won't be using them. It may not have a huge impact on smaller projects but if you have some method that is used excessively across an application then it's use is more applicable than just some new trendy style.
Discards are equivalent to unassigned variables; they do not have a value. Because there is only a single discard variable, and that variable may not even be allocated storage, discards can reduce memory allocations. Because they make the intent of your code clear, they enhance its readability and maintainability. They are basically a way to ignore local variables those are irrelevant to perform the action, it is like when you call a method that returns a value, however, you are interested only in the underlying operations it performs, you don't use the return of that method.
As we can see on the example below, discards are particularly useful in working with tuples when your application code uses some tuple elements but ignores others:
Note: I have got the example from Microsoft docs and I have included a second output just to show the difference
public class Example
{
public static void Main()
{
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
var (cityName, _, year1, pop3, _, pop4) = QueryCityDataForYears("New York City", 1980, 2010);
Console.WriteLine($"Population change, in 1960 to 2010: {pop2 - pop1:N0}");
Console.WriteLine($"Population change, in {cityName} from {year1} to 2010: {pop4 - pop3: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 (year1 == 1980)
{
population1 = 7981984;
}
if (year2 == 2010)
{
population2 = 8175133;
}
return (name, area, year1, population1, year2, population2);
}
return ("", 0, 0, 0, 0, 0);
}
}
//Output
//Population change, in 1960 to 2010: 393,149
//Population change, in New York City from 1980 to 2010: 193,149
In general, there is no sense to use discard in this sentence:
_ = nc.Read(someBuffer, 0, someSize);
since you can just ignore the return value. I did never anyone using standalone discards, but the documentation covers some specific cases when it can be useful.
The discards are used when you have to provide a variable, but don't need it.
As already covered in the provided documentation page, discards are very useful for:
switch (stream)
{
case MemoryStream _:
case FileStream _:
ProcessStream(stream);
break;
}
If you want to check if you can parse a string to an integer, but don't care about the result:
if (int.TryParse(str, out _))
{
// do something
}
Otherwise, you would have to declare an out variable, which you wouldn't use and which would consume some memory.
(_, _, area) = city.GetCityInformation(cityName);
Without discards you would deconstruct the result into 3 different variables, and use only one of them. With discards the code has cleaner intentions and more ineffective in terms of memory management.