Devlico.Us
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @devlicious

Christopher Bennage

Our WPF book is now available!


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.)



Comments

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# July 16, 2008 8:50 PM

Matt Hinze said:

There is some open source code that allows some neat stuff in this vein: http://www.lostechies.com/blogs/chad_myers/archive/2008/07/06/exploring-shadetree-features-part-1-static-reflection-with-reflectionhelper.aspx
# July 16, 2008 10:05 PM

Christopher Bennage said:

@Matt Yes, that's very similiar.  Here's a shorter version of the url, since it was cut off in the comment:

http://tinyurl.com/6kuly4

# July 16, 2008 10:19 PM

Zack Owens said:

You don't need Invoke. property(value); will work.
# July 16, 2008 10:28 PM

Arjan`s World » LINKBLOG for July 17, 2008 said:

Pingback from  Arjan`s World    &raquo; LINKBLOG for July 17, 2008

# July 17, 2008 8:17 AM

Will said:

Nice. How would this look as an extension method?
# July 17, 2008 9:20 AM

Christopher Bennage said:

@Zack Thanks!

# July 17, 2008 10:48 AM

Christopher Bennage said:

@Will, not very pretty actually.  I wouldn't make it an extension for Brush, because  it would further occlude the intent. You can make it an extension of Action<Brush>, but (as far as I understand) you can't call it inline on the lambda.  In other words, you can't do this:

(b => Fill = b).SetBrush(style.FillBrush)

You'd have to do this:

Action<Brush> lambda = brush => Fill = brush;

lambda.SetBrush(style.FillBrush);

# July 17, 2008 10:56 AM

Dew Drop - July 17, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - July 17, 2008 | Alvin Ashcraft's Morning Dew

# July 17, 2008 12:29 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Christopher Bennage

Christopher is a software developer and consultant at Blue Spire Consulting, a company he co-founded with Rob Eisenberg in 2006. He is a Christian, a marginal musician, and an armchair philosopher. His interests include programming, liberal education, science, truth, beauty, and a number of deceased British authors (C. S. Lewis, G. K. Chesterton, and most recently Owen Barfield.) He lives in Tallahassee, FL with his wife and three children and still prefers to play as the Night Elves in WarCraft 3. Check out Devlicio.us!

Our Sponsors

Red-Gate!