简体   繁体   中英

Decimal.TryParse in LINQ query - How to use the out parameter and avoid second conversion

I am using a LINQ to XML query to go through a XML file and collect those nodes where the balance is positive. The XML may have an empty balance node or contain content that cannot be converted to decimal so I have checks in place to skip such values. One of the checks used decimal.TryParse() to see if the content of the balance node can be converted to decimal. If it can be converted I have a followup Where clause that performs the conversion.

XML structure:

<Invoice>
...
  <balance>Could be any string here or empty</balance>
...
</Invoice>

Code:

decimal convertedDecimalButUnused;
var resultsWithPositiveBalance = xml.Descendants("Invoice")
.Where(x => !x.Element("balance").IsEmpty);
.Where(x => Decimal.TryParse(x.Element("balance").Value, out convertedDecimalButUnused))
.Where(x => Convert.ToDecimal(x.Element("balance").Value) > 0);

My question is if I can make use of the out parameter of the decimal.TryParse() instead of performing the decimal conversion a second time?

Just do the comparison in line with the TryParse. You can take advantage of the C#7 feature allowing you to declare the value in line.

For example:

var resultsWithPositiveBalance = xml.Descendants("Invoice")
.Where(x => !x.Element("balance").IsEmpty);
.Where(x => Decimal.TryParse(x.Element("balance").Value, out var val) && val > 0)

Since TryParse will handle if the element is empty for you already, you can forgo checking that as well. In the end, you can get the result you want with this:

var resultsWithPositiveBalance = xml.Descendants("Invoice")
.Where(x => decimal.TryParse(x.Element("balance").Value, out var val) && val > 0);

Yes you can do this:

decimal convertedDecimalButUnused;
var resultsWithPositiveBalance = xml.Descendants("Invoice")
.Where(x => !x.Element("balance").IsEmpty);
.Where(x => Decimal.TryParse(x.Element("balance").Value, out convertedDecimalButUnused) && convertedDecimalButUnused > 0);

You can chain multiple assertions together inside the Where function using && which equates to AND.

Try writing a utility extension method that converts XElement to Decimal. You may treat non-decimal values as zero in this case as you are interested only in positive values. If you want to differentiate between real 0 value and non-decimal value, then the method can return nullable decimal.

public static class UtilityExtensions {
    // return decimal? to differentiate between real zero and non-decimal values
    public static decimal ToDecimal(this XElement element){
        if(element == null || element.IsEmpty) return 0; // return null for decimal?
        decimal value;
        // if you can use C# 7, code can be more succinct with inline declaration
        return Decimal.TryParse(element.Value, out value) ? value : 0; // return null instead of 0 for decimal?
    }
}

Now your LINQ is much simpler. This will also handle the case where "balance" element itself is missing.

var resultsWithPositiveBalance = xml.Descendants("Invoice")
         .Where(x => !x.Element("balance").ToDecimal() > 0);

In case you end up using decimal? version:

var resultsWithPositiveBalance = xml.Descendants("Invoice")
         .Where(x => (!x.Element("balance").ToDecimal() ?? 0 ) > 0);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM