Showing posts with label Fluent API. Show all posts
Showing posts with label Fluent API. Show all posts

Friday, April 4, 2008

Enhancing C#'s switch statement with a fluent API

A colleague pointed me to this article today.  It shows a very clever set of utility classes that can dramatically improve the usefulness of the "switch..case" concept.  Since the C# language can't be extended, it is implemented as a fluent API that tries to mimic the built-in switch statement.

The most interesting use to me is the generics-based double dispatch using delegates that enables a sort of visitor pattern:

void Do(Control c) 
{
new Switch(c)
.Case<Label>(l =>
{
// ...
})
.Case<Button>(b =>
{
// ...
})
.Default(cc =>
{
// ...
});
}



An interesting oddity of the implementation is that the various Case() overloads are implemented as Extension methods on the Switch types. That is the only thing I don't like in the linked article. It exploits a "feature" of Extension methods that sorta allows you to call methods on null references. The case "statements" (method calls) are short-circuited by returning null and additional calls to Case() check for the null "this" pointer (the fake "this" which is the first parameter to extension methods) and exits quickly. To me, this seems like abuse of extension methods since the same thing can easily be accomplished with a traditional class.



Here is a modified version of the Switch<T> class implemented as a simple class without the extension methods:




public class Switch<T>
{
public T Object { get; private set; }
private bool hasBroken = false;

public Switch(T obj)
{
this.Object = obj;
}

public Switch<T> Case(T other, Action<T> action)
{
return this.Case(other, action, SwitchBehavior.Break);
}

public Switch<T> Case(T other, Action<T> action, SwitchBehavior behavior)
{
return this.Case(x => object.Equals(x, other), action, behavior);
}

public Switch<T> Case(Func<T, bool> condition, Action<T> action)
{
return this.Case(condition, action, SwitchBehavior.Break);
}

public Switch<T> Case(Func<T, bool> condition, Action<T> action, SwitchBehavior behavior)
{
if (!this.hasBroken && condition(this.Object))
{
action(this.Object);
if (behavior == SwitchBehavior.Break)
this.hasBroken = true;
}

return this;
}

public void Default(Action<T> action)
{
this.Case(t => true, action, SwitchBehavior.Break);
}
}

public enum SwitchBehavior { FallThrough, Break }



A functional C# (type)switch - B# .NET Blog