Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Using Lambdas to get at properties

This is a somewhat made-up scenario based on a recent project. My client has not given me permission to discuss the specifics of the application (yet), so I apologize for keeping it vague. I hope you don't give up before the payoff.

The Problem

Imagine an application, where a user can create "styles" and those styles can be applied to shapes. The application uses WPF to visualize the shapes with the styles applied.  (Perhaps it's a design tool meant for Hanselman's BabySmash?) Let's say that there are three kinds of shapes: a line, a circle, and a bull's eye.

styled_shapes Each shape has specific attributes that can be styled, and the these attributes are unique to each shape.  Here's a simple listing of possible attributes:

Line Circle Bull's Eye

Stroke

Fill

Outer Fill

Thickness

Stroke

Inner Fill

 

Now, we have three classes to represent the styles: LineStyle, CircleStyle, BullsEyeStyle. The Fill and Stroke attributes are all of type Brush. Furthermore, all three classes derive from ShapeStyle.

Now, imagine that we have to construct instances of these styles for some data that comes in the form of an API that we don't control. The constructor for LineStyle might look like this:

public class LineStyle : ShapeStyle
{
    public LineStyle(ForeignLineStyle style)
    {
        Thickness = style.StrokeThickness;
        Stroke = style.StrokeBrush;
    }

    public Brush Stroke { get; set; }
    public double Thickness { get; set; }
}

That's simple enough, but remember the properties are of type Brush, and we don't really want our instances of LineStyle to share the same Brush with instances of ForeignLineStyle.  Luckily, Brush has a Clone method! Yeah!  Only, what if the Brush is null? Okay, we'll change it to this:

public class LineStyle : ShapeStyle
{
    public LineStyle(ForeignLineStyle style)
    {
        Thickness = style.StrokeThickness;
        Stroke = (style.StrokeBrush != null)
                     ? style.StrokeBrush.Clone()
                     : null;
    }

    public Brush Stroke { get; set; }
    public double Thickness { get; set; }
}

Now, every time I have a property of type Brush on one of these style classes the pattern is the same.  I  start copying and pasting. (Do you smell something?)  Hmm, what if I have lot of Brush properties? Many more than I initially listed in the table. I sure feel like I am repeating myself.

At this point, my instinct is to extract this pattern into a method. But how do I do this? The properties are unique to each class, and using reflection would be way overkill.

Lambda Magic

What I want is a method that takes the brush from ForeignLineStyle, and the setter for a property of type Brush. Using a lambda for the setter, I am able to create:

public static void SetBrush(Brush brush, Action<Brush> property)
{
    var value = (brush != null)
            ? brush.Clone()
            : null;

    property.Invoke(value);
}

For this to make sense, you need to recognize that a setter is a essentially a method with a signature like this:

delegate void PropertySetter<T>(T value);

That signature is the same as Action<T>.

Now, I can modify LineStyle to look like this:

public class LineStyle : ShapeStyle
{
    public LineStyle(ForeignLineStyle style)
    {
        Thickness = style.StrokeThickness;
        SetBrush(style.StrokeBrush, brush => Stroke = brush);
    }

    public Brush Stroke { get; set; }
    public double Thickness { get; set; }
}

Notice that the second parameter is :

brush => Stroke = brush

and not simply

()=> Stroke

The latter is for the getter and would match Func<T>.

After Thoughts

Arguably, this doesn't accomplish much.  The repetitive code had a small footprint, and the SetBrush method is likely to throw off other developers.

It's up to you to decide it's applicability, but a least it's one more tool in the box.

(Thanks to Rob Eisenberg, who wrote some code that planted this ideas in my head.)


Posted 07-16-2008 8:36 PM by Christopher Bennage

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)