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