views:

290

answers:

6

This is a question about coding for readability.

I have an XDocument and a List<string> of the names of the elements that contain sensitive information that I need to mask (replace with underscores in this example).

XDocument xDoc;
List<string> propertiesToMask;

This can be written in two ways, using traditional foreach loops, or using .ForEach methods with lamba syntax.

foreach (string propertyToMask in propertiesToMask)
{
    foreach (XElement element in xDoc.Descendants(propertyToMask))
    {
        element.SetValue(new string('_', element.Value.Length));
    }
}

or

propertiesToMask
    .ForEach(propertyToMask => xDoc.Descendants(propertyToMask).ToList()
        .ForEach(element => element.SetValue(new string('_', element.Value.Length))));

Which approach do you think is the most readable, and why? If you prefer the second example, how would you present it for maximum readability?

+10  A: 

Eric Lippert has a good entry about this on his blog. To summarize, the very task done by ForEach is to produce side effects which may not be a desirable thing to do with the functional style of programming in C#.

Mehrdad Afshari
+1. This article was exactly what came to mind when I read the post. And your summary is excellent!
TrueWill
+12  A: 
foreach (string propertyToMask in propertiesToMask)
{
    foreach (XElement element in xDoc.Descendants(propertyToMask))
    {
        element.SetValue(new string('_', element.Value.Length));
    }
}

Because the spacing makes it very simple to scan. The second is far to cluttered and I have to actually read it.

corymathews
The second one looks just fine to me. Much less jumbled up.
Terry Donaghe
And for the reasons suggested in Mehrdad's post.
Robert Harvey
Readable code can be just as important as efficient code (as long as its not a code smell)
Jrud
I find this much harder to read than an implementation making good use of ForEach() while it is of course much easier to debug. Good use of ForEach() (usually) means avoiding nested ForEach() calls and using Select() and SelectMany() instead.
Daniel Brückner
+3  A: 

I strongly prefer the first, for three reasons.

First, it's more efficient (in the second, you have extra ToList() calls).

Secondly, it's more readable, in my opinion.

Finally, I'd recommend reading Eric Lippert's blog post on this subject. There are philosophical reasons to avoid List<T>.ForEach, as it's entire purpose is to cause side effects, even though it has a functional style.

Reed Copsey
A: 

Here's a really subjective answer:

I don't really agree with the philosophical reasoning behind not liking .ForEach. Maybe it's my lack of a computer science background, I don't know.

To me, the second set of code is easier to read and looks much less jumbled. As others have mentioned, the ToList() is kind of unfortunate, but it still just looks better to me.

I like Daniel Brückner's solution even better. It seems better than either of the other proposed solutions.

Terry Donaghe
+1  A: 

The first one can be altered while the debugger is running and Visual Studio allows you to continue debugging. After altering the .ForEach variant you have to restart the debugging session and recompile because it contains a lambda-expression (VS 2008)

Rauhotz
+4  A: 

The traditional way has the big advantage that it can be easily debugged. But I would personally prefer the ForEach() approach in this case. The circumstance that it is hard to debug code written in a fluent still is in my opinion a flaw of the available tools, not the coding style. In my personal experience the error rate in such methods is very low, hence it is no really big problem.

I would write some extension methods yielding the following code.

propertiesToMask
   .SelectMany(property => document.Descendants(property))
   .ForEach(element => element.MaskValue());
Daniel Brückner
I like this code a LOT better.
Terry Donaghe
I like fluent interfaces as well, but this example has side effects. Please see the posts by @Mehrdad and @Reed.
TrueWill
(Almost) All fluent interfaces cause side effects. I read Eric's post before and I understand his point not to introduce ForEach() together with all the LINQ methods free of side effects. But I don't think the method is bad per se. It has side effects. Many methods do. If you are used to this style, there is no danger of missing the side effects. The big point for me is the readability ... can you do (much) better? Even with a (hypothetical) syntax other then C# this seems to be close to the optimum for me.
Daniel Brückner
@Daniel: Personally, I'd use SelectMany to generate the IEnumerable<XElement>, but still use a traditional foreach for enumeration of setting the value.
Reed Copsey