views:

973

answers:

5

This may be quite simple but I'm rather new to Lambda's so bear with me.

I have a function that uses a Lambda function to recurse. The main function receives a bool telling it to include certain information or not within the lambda.

The function is designed to write out a custom class to XML - I think the code is pretty self explanitory.

At the moment I have overcome the problem using a simple if statement, but it feels ugly so wondered if anyone knew a better way?

        private XElement ErrorListToXml(ErrorList el, bool outputTagsOnly)
    {
        // Need to declare in advance to call within the lambda.
        Func<ErrorType, XElement> recursiveGenerator = null;

        if (outputTagsOnly)
            recursiveGenerator = error => new XElement
                (error.Name,
                 error.ChildErrors.Select(recursiveGenerator));
        else
            recursiveGenerator = error => new XElement
          (error.Name,
          new XAttribute("Ignore", error.Filter),
           error.ChildErrors.Select(recursiveGenerator));


        var element = new XElement
                   ("ErrorList",
                    ChildErrors.Select(recursiveGenerator));

        Console.WriteLine(element);

        return element;
    }
+5  A: 

You could move the "if" statement inside the lambda function safely, if you preferred:

Func<ErrorType, XElement> recursiveGenerator = null;

recursiveGenerator = (error =>
    outputTagsOnly
        ? new XElement(error.Name,
                       error.ChildErrors.Select(recursiveGenerator));
        : new XElement(error.Name, new XAttribute("Ignore", error.Filter),
                       error.ChildErrors.Select(recursiveGenerator)));

var element = new XElement("ErrorList", ChildErrors.Select(recursiveGenerator));

Other than that, there doesn't seem to be any trivial way to simplify what you've got.

(P.S. When it looks ugly, put some lipstick on that pig by pretty-printing it ;)

mquander
Thanks that was what I was looking for
Chris
Here performance is somewhat worse than in the original code: you check the flag outputTagsOnly on generation of _every_ node, while it's totally not necessary, you can check it once. (Of course for the small input data its insignificant)
Yacoder
Yes, I realized that, but the flag check is so vanishingly insignificant compared to the overhead of actually constructing XML elements that I think it is of absolutely zero importance compared to readability.
mquander
A: 

I guess you can do this, but end of the day it's still an if:

                recursiveGenerator = error => outputTagsOnly ? 
                   new XElement(error.Name,error.ChildErrors.Select(recursiveGenerator)
              : 
              new XElement(error.Name,new XAttribute("Ignore", error.Filter), 
             error.ChildErrors.Select(recursiveGenerator);
BFree
Cheers mate, mquander pipped you to the post. I know it is still an if I was just trying to find a more elegant way. Thanks!
Chris
+3  A: 

You can make a decision between values of the same type in a lambda pretty easily:

customer => flag ? customer.Name : customer.Address

You can use an if statement in a lambda with a little more effort:

customer =>
{
  if (flag)
    return customer.Name
  else
    return customer.Address
}

Neither of these helps your method greatly.

David B
A: 

You can try to decompose your problem into two different ones:

  1. How to build a tree from errors structure.
  2. What to put into the tree nodes.

Then the code will look like:

        private XElement ErrorListToXml(ErrorList el, bool outputTagsOnly)
        {
            // Need to declare in advance to call within the lambda.
            Func<ErrorType, XElement> treeGenerator = null;
            Func<ErrorType, object[]> elementParametersGenerator = null;

            treeGenerator = error => new XElement
                (error.Name,
                elementParametersGenerator(error));

            if(outputTagsOnly)
                elementParametersGenerator = error => 
                    new object[] {error.ChildErrors.Select(treeGenerator)};
            else
                elementParametersGenerator = error => 
                    new object[] { new XAttribute("Ignore", error.Filter), error.ChildErrors.Select(treeGenerator) };

            var element = new XElement
                       ("ErrorList",
                        ChildErrors.Select(treeGenerator));

            Console.WriteLine(element);

            return element;
        }

Not any significantly better in this particular case, but it's a more general approach.

Yacoder
+3  A: 

mquander's solution can be improved slightly to reduce duplication. You can use the fact that you can pass in null an element in the XElement constructor content, and it gets ignored. We can therefore move the condition further in:

Func<ErrorType, XElement> recursiveGenerator = null;    
recursiveGenerator = (error => new XElement(error.Name,
            outputTagsOnly ? null : new XAttribute("Ignore", error.Filter),
            error.ChildErrors.Select(recursiveGenerator));

var element = new XElement("ErrorList", ChildErrors.Select(recursiveGenerator));
Jon Skeet
+1, didn't know the constructor had that property.
mquander
Ahhh, this is how I originally tried to do it but didn't realise you could pass null either. Thanks
Chris