LINQ Walk[source]
xml
| <glacius:metadata> | |
|     <title>LINQ Walk</title> | |
|     <description>Implementation of a walk() extension method for LINQ</description> | |
|     <category>Legacy blog posts</category> | |
|     <category>Programming</category> | |
|     <category>C#</category> | |
| </glacius:metadata> | |
| <glacius:macro name="legacy blargh banner"> | |
|     <properties> | |
|         <originalUrl>https://tmont.com/blargh/2010/3/linq-walk</originalUrl> | |
|         <originalDate>2010-03-24T08:05:57.000Z</originalDate> | |
|     </properties> | |
| </glacius:macro> | |
| <p> | |
| So, we all know that | |
|   <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/">LINQ</a> | |
| is rad. Not in that silly faux-SQL syntax, but in the declarative, fluent syntax. | |
| </p> | |
| <p> | |
|   But the sad part is that there is no <code>walk</code> extension method. Walk is a  | |
| functional programming staple that iterates over a collection of stuff and applies a | |
| callback to each item. | |
| </p> | |
| <p> | |
|   You might be thinking, "but there is a <code>ForEach()</code> extension method on  | |
|   <code>List</code>!". And you would be wrong, because there is a subtle, yet important  | |
|   difference between <code>IEnumerable</code> and <code>IList</code>. | |
| </p> | |
| <p> | |
|   <code>IEnumerable</code> is late-binding. That means you can do whatever you want to  | |
| it, but execution is deferred until you actually enumerate the enumeration, i.e. using a | |
|   <code>foreach</code> construct. In a list, there is no deferred execution. This deferred  | |
| execution is accomplished through the magic of closures and expression trees. | |
| </p> | |
| <p>In the meantime, here is a fluent implementation of <code>walk</code>:</p> | |
| <glacius:code lang="csharp"><![CDATA[public static class LinqExtensions { | |
| 	public static IEnumerable<T> Walk<T>(this IEnumerable<T> source, Action<T> action) { | |
| 		foreach (var t in source) { | |
| action(t); | |
| yield return t; | |
| } | |
| } | |
| }]]></glacius:code> | |
| <p>You can prove that this is in fact using deferred execution with a simple test:</p> | |
| <glacius:code lang="csharp"><![CDATA[class Program { | |
| 	class Foo { | |
| 		public int Bar { get; set; } | |
| 		public override string ToString() { | |
| 			return string.Format("Foo(Bar={0})", Bar); | |
| } | |
| } | |
| 	static void Main() { | |
| 		IEnumerable<Foo> foos = new[] { new Foo { Bar = 2 }, new Foo { Bar = 1 }, new Foo { Bar = 3 } }; | |
| 		Console.WriteLine("IEnumerable.Walk():"); | |
| foos.Walk(Console.WriteLine); | |
| Console.WriteLine(); | |
| 		Console.WriteLine("List.ForEach():"); | |
| foos.ToList().ForEach(Console.WriteLine); | |
| Console.WriteLine(); | |
| Console.ReadLine(); | |
| } | |
| }]]></glacius:code> | |
| <p>The output looks like this:</p> | |
| <p class="text-center"> | |
|   <img glacius:src="linqwalk1.png" alt="LINQ walk output" /> | |
| </p> | |
| <p> | |
|   Notice that nothing got printed the first time through, using the <code>Walk()</code> extension  | |
| method. There's your deferred execution. That means our extension method did not enumerate the | |
| enumeration, so it's safe and efficient to use walk in a normal linq expression where you are | |
| depending on deferred execution, like this: | |
| </p> | |
| <glacius:code lang="csharp"><![CDATA[ | |
| IEnumerable<Foo> foos = new[] { new Foo { Bar = 2 }, new Foo { Bar = 1 }, new Foo { Bar = 3 } }; | |
| Console.WriteLine("Doing more stuff:"); | |
| var listOStuff = foos | |
| .Where(foo => foo.Bar >= 2) | |
| .Walk(foo => foo.Bar += 10) | |
| 	.Select(foo => new Baz { Foofy = foo }) | |
| .Walk(baz => baz.Foofy.Bar--) | |
| .OrderBy(baz =>; baz.Foofy.Bar); | |
| foreach (var stuff in listOStuff) { | |
| Console.WriteLine(stuff); | |
| } | |
| Console.ReadLine();]]></glacius:code> | |
| <p class="text-center"> | |
|   <img glacius:src="linqwalk2.png" alt="LINQ walk output 2" /> | |
| </p> | |
| <p>Well, maybe that's not "normal." Whatever.</p> |