Showing posts with label .NET. Show all posts
Showing posts with label .NET. Show all posts

Monday, March 17, 2008

Rob Relyea Hates my Mom

Mom, Dad, Kevin

My mother is a lovely woman. So I'm confused as to why Rob dislikes her so much.

Let me back up. Last August (when I was still at my former employer) I did some demos of new features in WPF 3.5 for Channel 9.

I was happy to see Rob reused some of the same demos for his MIX '08 Talk.

When I was watching the Add-In demo, I was surprise to see the data had remained the same--for the most part. My dad--Robert, my brothers--Brian and John, and I were still in the data set.

image

But my mother--Wynette--replaced by "Web Service". I mean, I know WCF is powerful, but it's a poor replacement for the most wonderful woman in the world--even if she has an uncommon name.

I did find it funny that the last name was left unchanged.

image

So come on Relyea. Let's hear it. I think I deserve an explanation. :-)

Friday, March 14, 2008

Reader Question: "Why WrapperElement<T> instead of Decorator?"

Yuval, via a comment, asks:

Can you explain why you chose to define WrapperElement<T> instead of simply inheriting from Decorator? They seem to be doing the same thing.

Ah, it is good to read my post "Don't subclass a Panel, unless you're making a Panel".

First, where do I used WrapperElement<T>? Well, at the moment, it's used in three places: FlipTile3D, Set, and Transition3D. In all cases it simply wraps a Viewport3D.

Now, could I simply use Decorator? Sure. I could subclass Decorator and do basically the same thing. Even easier: I could subclass Viewport3D.

Why don't I?

It's all about API usability and correctness.

When I write a control (especially for the bag-o-tricks) I try to make it "right".

If these controls subclassed Viewport3D, they should act like Viewport3D. Meaning, they should support someone added arbitrary lights, models, etc. They should support messing with and removing elements that are already added.

I don't want this.

Wrapping everything with a Decorator, just exchanges problems.

If I subclass Decorator, it would imply that I mean people to treat the control like a Decorator, which means accessing and changing the Child property.

I don't want this, either.

WrapperElement<T> does some very simple things.

  • Stores a Framework element as a child visual.
  • Adds it to its visual tree (via AddVisualChild)
  • overrides GetVisualChildCount
  • overrides VisualChildCount
  • overrides MeasureOverride
  • overrides ArrangeOverride
  • Provides protected access to the child so the subclass can play with it, but not the end user.

One should use WrapperElement<T> when one wants to use the implementation of an existing component--Viewport3D, Grid, etc.--but doesn't want the object model of that component exposed.

Make sense?

Thanks for the question, Yuval.

Happy hacking.

Saturday, March 8, 2008

Bag-o-Tricks March '08 Edition

January 12 was my last update to the bag-o-tricks. I couldn’t let two months go by without another refresh. There is a lot of stuff here I've already talked about. The code has been available via SVN for a while, but not in the convenient package.

The Goods

Binaries and Source [zip, 4.17MB]. Tested with Visual C# Express 2008.

The Details

  • VS Copy-to-HTML - New!
    • Blogged about it here.
  • NotifyWorker - New!
    • Blogged about it here.
  • HueConverter - New!
    • Makes it easy to create a pretty spectrum of colors for a number of items
  • Animating Tile Panel
    • Renamed AnimateNewItem property to AnimatesNewItem
    • Made initial load not animate new items
    • Polished the demo code
  • CompositionTargetRenderingListener
    • Added Dispose and used it where appropriate
    • Added WireParentLoadedUnloaded helper method
  • DemoCollection
    • Added Reset command
    • Moved to library assembly
  • Extentions
  • FlipTile3D
    • Moved to WrapperElement<Viewport3D>
  • FolderPicker
    • Moved to new SelectRecursive implementation
  • WPFUtil
    • Added HSB-RGB converter
    • Created ‘Animate’ methods to abstract physics model. Moved FileTile3D, AnimatingTilePanel, and ZapDecorator to these.
  • ZapScroller
    • A lot cleaner. Removed a completely unnecessary layer of complexity.
    • Added the ability to template the buttons. Thanks for the suggestion, Atul.
  • WPF Set
  • General
    • A bunch of renaming of namespaces. Most of the new stuff will exist under J832.Wpf

As always, the SVN location has been updated. Patches are always welcome. (I've had one taker on this. Very cool to do an apply patch. Would love to do it again.)

Enjoy and happy hacking!

Tuesday, February 5, 2008

Xaml Serialization with WCF

So I'm reading RESTful Web Services and I got inspired. Sounds like a great way to expose data on the web.

I've heard great things about WCF support for REST, with their new WebGet attribute and such.

So I fire up Visual C# Express and get hacking.

Amazing what one can do in 54 lines of code.

using System;
using System.ServiceModel;
using System.ServiceModel.Web;
 
namespace J832
{
class Program
{
static void Main(string[] args)
{
Time time = new Time();
 
using (WebServiceHost host = new WebServiceHost(time, new Uri("http://localhost:8080/")))
{
host.AddServiceEndpoint(typeof(ITime), new WebHttpBinding(), "time");
host.Open();
 
Console.WriteLine("enter to close...");
Console.ReadLine();
}
}
}
 
[ServiceContract]
public interface ITime
{
[OperationContract, WebGet]
DateTimeCount GetTime();
}
 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Time : ITime
{
public DateTimeCount GetTime()
{
return new DateTimeCount(DateTime.Now, m_count++);
}
 
private int m_count;
}
 
[Serializable]
public class DateTimeCount
{
public DateTimeCount(DateTime now, int count)
{
Now = now;
Count = count;
}
 
public DateTime Now { get; set; }
public int Count { get; set; }
}
}
 

And we open a browser to http://localhost:8080/time/GetTime:

<DateTimeCount xmlns="http://schemas.datacontract.org/2004/07/J832" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<_x003C_Count_x003E_k__BackingField>0</_x003C_Count_x003E_k__BackingField>
<_x003C_Now_x003E_k__BackingField>2008-02-04T23:28:21.987561-08:00</_x003C_Now_x003E_k__BackingField>
</DateTimeCount>

Isn't that *cough* *choke* pretty *gag*.

I suddenly had a flash of nostalgia...of longing...for Xaml.

Xaml: that wonderful language where document-object mapping is implicit, trivial, and human-understandable.

(I'd love to link to Our 7 Goals of Xaml introduction but the link is dead. Help, Rob?)

Thankfully, XamlWriter is publicly available and WCF is insanely, gratuitously, wonderfully extensible.

  • WCF services have endpoints.
  • Endpoints have contracts.
  • Contracts have operations.
  • Operations have behaviors.
  • Behaviors have formatters.
  • Formatters can have custom messages--for instance, XamlMessage.

Thanks to Maheshwar Jayaraman for the post (and sample) on his old blog that got me started.

The code is a bit longer, but it's mostly boiler plate:

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Windows.Markup;
using System.Xml;
 
[assembly: XmlnsDefinition("http://schemas.j832.com/demo/2008", "J832")]
 
namespace J832
{
class Program
{
static void Main(string[] args)
{
Time time = new Time();
 
using (WebServiceHost host = new WebServiceHost(time, new Uri("http://localhost:8080/")))
{
ServiceEndpoint endPoint = host.AddServiceEndpoint(typeof(ITime), new WebHttpBinding(), "time");
foreach (OperationDescription operationDescription in endPoint.Contract.Operations)
{
operationDescription.Behaviors.Add(new MyOperationBehavior());
}
 
host.Open();
 
Console.WriteLine("enter to close...");
Console.ReadLine();
}
}
}
 
[ServiceContract]
public interface ITime
{
[OperationContract, WebGet]
DateTimeCount GetTime();
}
 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Time : ITime
{
public DateTimeCount GetTime()
{
return new DateTimeCount(DateTime.Now, m_count++);
}
 
private int m_count;
}
 
[Serializable]
public class DateTimeCount
{
public DateTimeCount(DateTime now, int count)
{
Now = now;
Count = count;
}
 
public DateTime Now { get; set; }
public int Count { get; set; }
}
 
public class MyOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
 
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
 
public void ApplyDispatchBehavior(
OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new MyMessageFormatter();
}
 
public void Validate(OperationDescription operationDescription) { }
}
 
public class MyMessageFormatter : IDispatchMessageFormatter
{
public void DeserializeRequest(Message message, object[] parameters) { }
 
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
return new XamlMessage(result);
}
}
 
public class XamlMessage : Message
{
public XamlMessage(object value){ Value = value; }
 
public override MessageHeaders Headers
{
get { return new MessageHeaders(MessageVersion.None); }
}
 
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
XamlWriter.Save(Value, writer);
}
 
public override MessageProperties Properties{ get{ return new MessageProperties(); } }
 
public override MessageVersion Version{ get{ return MessageVersion.None; }}
 
public object Value { get; private set; }
}
}

And (drumroll) the output from http://localhost:8080/time/GetTime:

<DateTimeCount Now="2008-02-04T23:53:18.884273-08:00" Count="14" xmlns="http://schemas.j832.com/demo/2008"/>

Isn't that pretty? The XmlnsDefinition assembly attribute is the icing that gives you the pretty xmlns.

Do other folks love Xaml? Do you wish it was available outside PresentationFramework.dll?

Let me know. I'm thinking of starting a campaign.

Hear that, Rob? :-)


kick it on DotNetKicks.com

Wednesday, January 30, 2008

WPF Set: Now in 3D!

I last discussed Set about 3 weeks ago. At the time, my implementation was based on buttons. It worked fine, but when I saw how it looked on a friend's machine running the 'classic' theme I was appalled.

I got thinking: there has to be a way to make this game more interesting.

The answer: 3D and gratuitous animation.

The address for the XBAP hasn't changed: http://j832.com/workBlogFiles/WpfSet/

WPF Set - 3D!

The source code has been checked-in to my SVN repository. The code files actually live under J832.Wpf.BagOTricksApp, but you can build and play with the stand-alone app by loading the J832.WpfSet solution file. (Thank you, CSProj file linking feature.)

Cool stuff:

  • The listbox on the right that displays the matched sets now uses AnimatingTilePanel.
  • First use of the new WPFUtil class, which exposes a set of Animate methods. These basically formalize and abstract my simple attraction-based physics model. I support 1-dimensional (double), 2-dimensional (Point/Vector), and 3-dimensional (Point3D/Vector3D) animation. Check out ZapScroller and AnimatingTilePanel in the source to see the other places these are used.
  • I've also added some helper extension methods to WPFUtil. Very useful.
  • Implemented a couple of super-simple 3D components: ButtonBase3D and ToggleButton3D. An interesting exercise. Hoping to move FlipTile3D to these in the future.

Not cool stuff:

  • Keyboard accessibility has evaporated. With the previous implementation, you could tab around to the buttons and press spacebar to select/unselect a "card". More work than I signed up for in this iteration, but something to keep in mind.
  • WPF handles z-order great in 3D unless you have transparency in your textures. Take a look at the corners of raised cards. Very frustrating. The only work-around is to manually re-order the child elements. Lame.
  • Perf is great...on my super fast workstation. Even on my relatively beefy laptop, I notice some glitching when things are moving around. Ideally, I'd detect machines hardware limitations and degrade the animations. I should probably offer a "turn off the fancy stuff" check box. Eh, V3.

This will of course be included in the next drop of the full bag-o-tricks, but I thought it was cool enough to share out-of-band.

Have fun.

Sunday, January 27, 2008

Improving NotifyWorker (and why blogging makes me smarter)

My current day job consists of writing middleware and web-based LOB software for an actuarial services company.

David is my primary collaborator and a fellow blogger. We get along amazingly well, but every once in a while, we disagree. The amazing thing, though, is how we handle disagreements.

I'm at a point where I look forward to disagreement! For a reason that I hope more people can come to realize.

If we disagree about how something should work, it's almost always an opportunity for improvement. Specifically, at least one of the following is likely to happen:

  • I get smarter
  • He gets smarter
  • The code gets better

Quite often, it's all three. I know this sounds like motivational speaker talk or corporate training fodder, but it's true. The fact that we often discuss our disagreements with explicit reference to masters is also helpful.

I blog for similar reasons. I'm far from a CLR, C# or WPF expert, at least relative to the experts I've had the opportunity to work with.

I'm just a geek trying to figure things out and sharing as I go. Hopefully what I share is helpful. Often, though, blogging has self-serving benefits. Just taking the time to write down explanations for what I'm doing forces me to rationalize edge cases.

If I had never blogged the recursive flattening craziness, I would have never have seen all of the edge cases I had missed the first time out.

Anyway, to my point.

Simon was kind enough to point out a deadlock in my NotifyWorker code.

FYI: At this point, looking at the code will be quite helpful.

I was doing a Thread.Join() in the middle of a lock. Of course, if the thread in question is waiting on the lock which is held, the thread will never exit. The result: the Join() call will never return.

Deadlock, indeed.

Props to Simon for pointing out this problem.

I moved the Join() outside of the lock and thought I'd fixed the problem.

Maybe, but I also realized another problem.

  • Dispose is likely called from the UI thread.
  • Dispose calls Join on my worker thread as discussed above.
  • The worker thread does Dispatcher.Invoke twice.

What happens if Dispose is called between my check for m_disposed and when the Invoke actually gets to the UI thread?

Dispose will be tying up the UI thread waiting for Join() to return. The worker thread will never terminate because it's waiting for Invoke to get to the UI thread.

Deadlock again.

Darn.

The solution: Move from Dispatcher.Invoke to Dispatcher.BeginInvoke.

BeginInvoke returns an instance of DispatcherOperation. This class has an Abort method which can be used to abort a pending BeginInvoke.

I now have an instance field containing the pending operation, if one exists, which can be aborted via Dispose before the call to Join. I also now enforce that Dispose must be called on the UI thread (by adding a call to VerifyAccess). This ensures that Abort is not called while the worker is synchronized to the UI thread.

After some careful uses of DisaptcherOperation.Wait and some paranoid null checks, I think I'm okay. 

As an aside, I also noticed that Dispose() will throw in the case where NotifyNewWork() has never been called. I fixed that, too.

*Sigh*

Threading code is tough. I had to stand up and pace around a few times as I mentally worked through possible edge cases.

As I've said before, I think this implementation is better, but it would be a stretch for me to claim it's correct. Hopefully, it's more correct.

Please keep the questions, comments, and feedback coming (after you take a look at the post about contacting me).

The code will get better and we'll all get smarter.

 

Happy hacking.

Tuesday, January 22, 2008

Responsive UI with Unresponsive Data: NotifyWorker

Yes, I finally got off my SelectRecursive kick. This is penance.

The 3-box Visio Diagram

How many user interfaces do you design that follow this pattern?

image

Okay, almost all of them.

A frequent refrain from users (well, at least me):

Regardless of what you're doing behind-the-scenes, the interface should never hang, stutter, or lag. Ever.

Easily said, right?

Well, while hacking on one of my side projects, I ran into this problem.

I put on my "framework guy" hat and asked, "Can I formalize this into a pattern?"

My answer: NotifyWorker.

Now before I get into NotifyWorker, I should mention BackgroundWorker: a frequently used and well thought-out component supplied by our friends in the Baseclass Library (BCL) for this kind of thing.

The trick performed by BackgroundWorker is simple, but important: allow start/stop/cancel of a task on another thread. It also keeps developers sane who have to deal with strict rules about what can only be done on the UI thread.

The type of tasks for which NotifyWorker was designed fit into a special niche that I think BackgroundWorkre misses:

  • Tasks that are long enough to lag the interface (0.1+ seconds) but are too short to require tracking of percent complete (so shorter than, say, 5 seconds).
  • Tasks that are repeated constantly as the UI updates. Think of recalculating a math/physics model or checking a server-hosted word list for a type-a-head find control.
  • Tasks where the work to be done is--in relative terms--longer than the work to update the user interface. If the task takes 0.1 seconds, but updating the 1,000 controls in your window takes 2 seconds, you should start by tweaking your architecture before using NotifyWorker.

How does it work?

(Reflector is how I learn all of my new APIs.)

image

The constructor takes three parameters that outline the diagram above and the workings of NotifyWorker:

  • Func<bool> prework: this is a delegate that is called on the Dispatcher (UI) thread in a WPF application. Delegate passing is a trick I use a lot, and you should, too. The method does the work to take interesting parameters from the UI (control values, etc.) and puts them in a spot where NotifyWorker can safely play on a non-UI thread. If you decide there is nothing interesting to do, you can return false, telling NotifyWorker "never mind".
  • Action work: this is where the magic happens. This function is called on a background thread which will leave your UI nice and responsive. Be super careful that 'work' only touches state (fields, etc) that you've safely set aside during prework.
  • Action postWork: the last box in our diagram. It takes the work done in 'work' and updates the UI accordingly. This method, as well as 'preWork', synchronizes the UI and background threads, so you don't have to worry about locking.

How do you kick things off? Call NotifyNewWork. This method can be called often, if you want. Put it in the 'Changed' handler of your favorite Slider or TextBox. If NotifyWorker is idle, it will wake up and immediately call preWork. If NotifyWorker is busy, it'll loop right around to get new input values as soon as postWork is done.

A demo

image

Sexy, huh?

The scenario is simplistic and contrived, like so many good demos.

Both sets of UI do the same thing: take the values from two sliders and sum them. Not too complicated.

For some strange reason, the sum 'routine' takes 0.25 seconds to return a result.

Maybe it's that new math...

Eh, it's probably the Thread.Sleep(250) I put in the method.

Anyway, use your imagination. You'll notice that the 'slow' UI is, well, slower. It lags a bunch as you drag the sliders since it's calling the expensive sum method on the UI thread.

The NotifyWorker controls do much better because the expensive call is hidden off-thread.

Simple, but useful.

Odds and ends

You'll notice a LastClientExceptionEventArgs property and a ClientException event. These are designed to make it a bit easier to handle non-critical exceptions during 'work'. The demo code actually shows this off pretty well.

You'll also notice that NotifyWorker implements IDisposable. Dispose will make sure to end any background work when you're done with NotifyWorker.

Under the covers, NotifyWorker uses LockHelper and some of the pulse/monitor tricks I wrote about a few months ago.

That's about it.

Since NotifyWorker is hard-wired to the notion the Dispatcher, it lives in J832.Wpf.BackOTricksLib. The demo is in the Bag-o-Tricks app under NotifyWorker.

I'm not going to do an official release (zip with binaries/source) of the bag for this sample. It's cool, but not cool enough to warrant a whole new drop.

It also gives you an excuse to play with SVN, if you haven't already. You can always just manually download the code from my SVN share using your browser, too.

Let me know if this works well for you.

 

Happy multi-threaded hacking!

 

Note: I've had some funky auth issues with Firefox on the SVN server lately. Let me know if you have any trouble.

Friday, January 18, 2008

Source code, HTML, and an Exercise for the Reader

No clue why I want to be vague about this. Probably because I'm late for a party, but really wanted to share.

And you're smart. You'll figure it out.

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="VS Copy-Paste to HTML" >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <RichTextBox Name="m_richTextBox" TextChanged="m_richTextBox_TextChanged" />
        <TextBox Grid.Column="1" Name="m_textBoxHTML" />
    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Web;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void m_richTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        m_textBoxHTML.Clear();
        m_textBoxHTML.AppendText("<div style=\"font-family:monospace;\">" + Environment.NewLine);
        foreach (Paragraph paragraph in m_richTextBox.Document.Blocks.Cast<Paragraph>())
        {
            foreach (Run run in Decompose(paragraph.Inlines))
            {
                m_textBoxHTML.AppendText(toCleanedColoredSpan(run));
            }
            m_textBoxHTML.AppendText("<br/>" + Environment.NewLine);
        }
        m_textBoxHTML.AppendText("</div>");
    }

    private string toCleanedColoredSpan(Run run)
    {
        SolidColorBrush solidColorBrush = run.Foreground as SolidColorBrush;
        if (solidColorBrush == null)
        {
            return cleanString(run.Text);
        }
        else
        {
            if (solidColorBrush.Color == Colors.Black)
            {
                return cleanString(run.Text);
            }
            else
            {
                string colorString = solidColorBrush.Color.ToString();
                Debug.Assert(colorString.Length == 9);

                colorString = colorString.Substring(3);

                return string.Format("<span style=\"color:#{0};\">{1}</span>",
                    colorString, cleanString(run.Text));
            }
        }
    }

    private string cleanString(string source)
    {
        source = HttpUtility.HtmlEncode(source);
        source = source.Replace(" ""&nbsp;");
        return source;
    }

    private static IEnumerable<Run> Decompose(IEnumerable<Inline> inlines)
    {
        foreach (Inline inline in inlines)
        {
            if (inline is Span)
            {
                foreach (Run run in Decompose(((Span)inline).Inlines))
                {
                    yield return run;
                }
            }
            else if (inline is Run)
            {
                yield return (Run)inline;
            }
            else
            {
                throw new NotSupportedException();
            }
        }
    }
}

Recursion, Linq, etc. - 3rd Times the Charm

The joke about Microsoft is that it always takes them three times to get it right.

If you look at my posts on the game Set in WPF, it would seem I follow the same pattern.

Well, here I go again.

Let's start simple: I have a path to a directory and I want to enumerate over all of it's subdirectories recursively. To be super concrete:

string[] startPaths = new string[] { @"D:\j832_svn\PublicSoftware\" }; 
IEnumerable<string> subPaths = SelectRecursivePaths(startPaths);

Easy enough.

What's the scenario-specific implementation of SelectRecursive? Well, our friend System.IO.Directory provides what we need.

private static IEnumerable<string> SelectRecursivePaths(IEnumerable<string> paths)
{
    foreach (string path in paths)
    {
        yield return path;
        foreach (string subPath in SelectRecursivePaths(Directory.GetDirectories(path)))
        {
            yield return subPath;
        }
    }
}

This code does the job for doing an in-order, recursive traversal of all of all of the directories. But it isn't very Linq-ish, is it?

Getting Linq-ish: SelectRecursiveSimple

Could there be a way to extract out the call to Directory.GetDirectories such that this method could be used over any homogenous, hierarchical structure? Yup.

public static IEnumerable<TSource> SelectRecursiveSimple<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TSource>> recursiveSelector)
{
    foreach (TSource element in source)
    {
        yield return element;
        foreach (TSource subElement in recursiveSelector(element).SelectRecursiveSimple(recursiveSelector))
        {
            yield return subElement;
        }
    }
}

Now we change the call site to embody the call to GetDirectories:

IEnumerable<string> subPaths = startPaths.SelectRecursiveSimple(path => Directory.GetDirectories(path));

I love lambda expressions and extension methods.

With me so far?

Getting Clever: SelectRecursiveClever

Now we go back to my clever idea from Tuesday and Wednesday about flattening recursion. The code I ended up with:

public static IEnumerable<TSource> SelectRecursiveClever<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TSource>> recursiveSelector)
{
    Stack<TSource> stack = new Stack<TSource>();
    source.Reverse().ForEach(stack.Push);
    while (stack.Count > 0)
    {
        TSource current = stack.Pop();
        yield return current;
        recursiveSelector(current).Reverse().ForEach(stack.Push);
    }
}

Note: ForEach is an extension method I created that does what Array.ForEach does: calls an Action<T> delegate on each item. In this case, pushing every item in the sequence into the stack.

Now the caller-side behavior of this is exactly the same as the "simple" version above. The implementation tries to be "clever" though, by discarding the "naive" use of recursion by replacing the call stack with a local stack. I was quite proud of this implementation. As I said on Tuesday:

If you can flatten a recursive implementation, you will have a better understanding of what's really going on under the covers.

Gulp. Perhaps I spoke to soon.

The Problem with Clever

I showed a co-worker this code. It took a bit of work to explain the use of .Reverse(). Stacks are Last-in-first-out, so if you want to get the first item out first, you have to put it in last.

Explaining this hurt my head a bit. In the "simple"--read "naive"--implementation, one didn't have to reverse the enumeration. Why should the flattened version be different?

The answer: it shouldn't.

At some point in the "clever" implementation, every item is on the stack. This means if you have a very wide tree, the stack could grow to be huge.

Imagine your recursiveSelector is smart about not loading all items at a given level into memory at one time--the benefit of sequences, right? It doesn't matter, because SelectRecursiveClever will load everything in one level into a stack. It trades the potential benefit of a smaller call stack with the potential blow-up of your working set when iterate over a folder with 10,000 sub-folders.

Oops.

You have to keep track of your depth in the recursion somewhere: either the runtime stack or your own stack.

But all you have to keep track of is where you are in each sequence at each level. You don't have to queue up all of the items at a given level.

Turns out the CLR has this obscure feature to handle this: IEnumerator<T>.

Yeah, not so obscure.

It's actually what the simple version of this code does. When you call down into the nested SelectRecursiveSimle, an IEnumerator<T> will be waiting patiently at the the current spot for the call to return.

Getting Smart(er): SelectRecursive

The code is more ugly, but it also does a better job of illuminating what is happening in the truly recursive, "simple" version.

public static IEnumerable<TSource> SelectRecursive<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TSource>> recursiveSelector)
{
    Stack<IEnumerator<TSource>> stack = new Stack<IEnumerator<TSource>>();
    stack.Push(source.GetEnumerator());

    while (stack.Count > 0)
    {
        if (stack.Peek().MoveNext())
        {
            TSource current = stack.Peek().Current;
            yield return current;
            stack.Push(recursiveSelector(current).GetEnumerator());
        }
        else
        {
            stack.Pop();
        }
    }
}

2008-01-19: Turns out this code is wrong, too.

The stack now holds on to what it should: the IEnumerator<T> at each level. The while loop runs through IEnumerator<T>.MoveNext(), going deeper with every stack.Push() until there are no more items and then "returning" with stack.Pop().

Popping the stack

It's pretty easy to implement a recursive construct using Linq. The first "simple" implementation works great and it's really useful when partying over the file system or any other hierarchy.

I still claim that flattening recursive code is a good thing to do for reasons I mentioned on Tuesday.

Just make sure you do it correctly.

Put another way: pay attention to your masters.

If you can't be smart, be simple.

Don't ever be clever.

Clever will burn you.

 

Happy hacking.

 

kick it on DotNetKicks.com

PS: SVN has been updated again with the latest code. Hopefully the last time I'll touch RecursiveSelect for a while.

Wednesday, January 16, 2008

Flattening recursive algorithms with Linq

I wake up this morning to find a comment from Stefan regarding my post yesterday about flattening recursion:

In the future version of C# we may receive a new language construct yield foreach which will eliminate this problem. Read this paper by Erik Meijer or this blog post by Wes Dyer if you are interested.

Ironically, I had read the paper and associated blog post last week.

And it got me thinking: could I abstract out my recursive traversal?

Even better: could I abstract it out in a way that was Linq-friendly?

The answer to both: oh yeah.

Check out the browser-friendly source here.

Now the code to find all of the selected directories becomes

SubDirectories
.SelectRecursive(directory => directory.SubDirectories)
.Where(directory => directory.IsSelected);

How's that for straight-forward?

Note 1: The savvy reader will notice that the third implementation is now like the original: the target node is not included in SelectedDirectories if it is selected. I like this more.

Note 2: Since I made SelectRecursive more general, I'm not quite as efficient about creating a bunch of enumerable instances. I still (likely) save a lot in call stack space and having a general implementation is pretty cool.

Note 3: You'll notice that SelectRecursive handles the case where there recursiveSelector returns null. This is different than how default extensions like SelectMany work. It made my usage in this scenario cleaner, but it's important to note. Or not. This bothered me for reasons I'll go into in another post. The code as checked-in now throws if null is returned. These subtle implementation details can cause headaches later.

As I mentioned yesterday, you can get the full source code from my SVN repository. The new methods extension methods have been added to Extensions.cs in J832.Common.

Happy recursive Linq-ing!