In a tidy bit of paralipsis, I’m not going to mention “closures”, “currying”, or “partial function application” again. Instead, I will provide a real-world example of what you can do with C# 3.0 and how it can help solve your problems.
I will use Visio in this example because I am familiar with writing code to automate it, but you won’t need any knowledge about Visio to understand this post.
Let’s start off what the output I want. Simply this will be a rectangle with a circular gradient fill that goes from red to blue:
If I manually draw this in Visio 2007 it will look like this:
Now, what I want to do is to programmatically draw this shape instead of manually drawing it. So I write this code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using IVisio = Microsoft.Office.Interop.Visio; namespace DemoCurrying { class Program { static void Main(string[] args) { var visapp = new Microsoft.Office.Interop.Visio.ApplicationClass(); var doc = visapp.Documents.Add(""); var page = visapp.ActivePage; var shape1 = page.DrawRectangle(2, 2, 6, 8); setfill1(shape1); } static void setfill1(IVisio.Shape shape) { // set the fill foreground color to red shape.get_CellsSRC( (short)IVisio.VisSectionIndices.visSectionObject, (short)IVisio.VisRowIndices.visRowFill, (short)IVisio.VisCellIndices.visFillForegnd) .FormulaU = "RGB(255,0,0)"; // set the fill background color to blue shape.get_CellsSRC( (short)IVisio.VisSectionIndices.visSectionObject, (short)IVisio.VisRowIndices.visRowFill, (short)IVisio.VisCellIndices.visFillBkgnd) .FormulaU = "RGB(0,0,255)"; // set the fill background pattern to radial shape.get_CellsSRC( (short)IVisio.VisSectionIndices.visSectionObject, (short)IVisio.VisRowIndices.visRowFill, (short)IVisio.VisCellIndices.visFillPattern) .FormulaU = "40"; } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using IVisio = Microsoft.Office.Interop.Visio;
namespace DemoCurrying { class Program { static void Main(string[] args) { var visapp = new Microsoft.Office.Interop.Visio.ApplicationClass(); var doc = visapp.Documents.Add(""); var page = visapp.ActivePage; var shape1 = page.DrawRectangle(2, 2, 6, 8); setfill1(shape1); }
static void setfill1(IVisio.Shape shape) { // set the fill foreground color to red shape.get_CellsSRC( (short)IVisio.VisSectionIndices.visSectionObject, (short)IVisio.VisRowIndices.visRowFill, (short)IVisio.VisCellIndices.visFillForegnd) .FormulaU = "RGB(255,0,0)";
// set the fill background color to blue shape.get_CellsSRC( (short)IVisio.VisSectionIndices.visSectionObject, (short)IVisio.VisRowIndices.visRowFill, (short)IVisio.VisCellIndices.visFillBkgnd) .FormulaU = "RGB(0,0,255)";
// set the fill background pattern to radial shape.get_CellsSRC( (short)IVisio.VisSectionIndices.visSectionObject, (short)IVisio.VisRowIndices.visRowFill, (short)IVisio.VisCellIndices.visFillPattern) .FormulaU = "40";
}
Let’s take a moment to examine the setfill1() method and see what we notice about it…
Now let’s focus on the repetitive parts. These are highlighted in red…
The parts that are not repeated are essentially the cell index and the value for the cell:
In generally we don’t want to repeat ourselves in code. What we do want in this case is to minimize our code to the unique parts (the three pairs of values shown above).
So what are our options?
I’ll focus on Option #4 only because it will illustrate how to use the new C# features. No criticism is implied against the other options.
In some sense, essentially what we are going to do is create a new function at runtime. This function will “remember” repetitive parts (shape, section index, row index), allowing us to worry about the non-repetitive parts.
Attempt #1
It’s imagine what we want to final code to look like
static void setfill3(IVisio.Shape shape) { var func_set_cell = MAKE_SETCELLFORMULA_WITH_SHAPE_SEC_ROW(shape, IVisio.VisSectionIndices.visSectionObject, IVisio.VisRowIndices.visRowFill); func_set_cell(IVisio.VisCellIndices.visFillForegnd, "RGB(255,0,0)"); // set the fill foreground color to red func_set_cell(IVisio.VisCellIndices.visFillBkgnd, "RGB(0,0,255)"); //set the fill background color to blue func_set_cell(IVisio.VisCellIndices.visFillPattern, "40"); // set the fill background pattern to radial }
the first line in red creates a new function that “remembers” the repetitive parts (the shape, the section index, and the row index). The new function is stored in a variable called “func_set_cell”
And then in the remaining three lines we call func_set_cell with just the unique pieces of data to get the same effect
Now what about this method called “MAKE_SETCELLFORMULA_WITH_SHAPE_SEC_ROW”? How can it dynamically create a new function that remembers these parameters?
static Action<IVisio.VisCellIndices, string> MAKE_SETCELLFORMULA_WITH_SHAPE_SEC_ROW(IVisio.Shape shape, IVisio.VisSectionIndices sectionindex, IVisio.VisRowIndices rowindex) { Action<IVisio.VisCellIndices, string> new_action = (IVisio.VisCellIndices cellindex, string value) => { var cell = shape.get_CellsSRC((short)sectionindex, (short)rowindex, (short)cellindex); cell.FormulaU = value; } ; return new_action; }
static Action<IVisio.VisCellIndices, string> MAKE_SETCELLFORMULA_WITH_SHAPE_SEC_ROW(IVisio.Shape shape, IVisio.VisSectionIndices sectionindex, IVisio.VisRowIndices rowindex) { Action<IVisio.VisCellIndices, string> new_action = (IVisio.VisCellIndices cellindex, string value) => { var cell = shape.get_CellsSRC((short)sectionindex, (short)rowindex, (short)cellindex); cell.FormulaU = value; } ;
return new_action; }