Introduction
One of the measures of my efficiency as a developer is how quickly I can spit
out well-written, verified code. This challenge is typically met with many
mechanisms: code generation programs, code snippets and even the rudimentary cut
& paste. In this article, I want to talk about Visual Studio code snippets and
the generative snippet mechanism which I use in order to squeeze the maximum
value out of snippets. I will also present a showcase of some of the generative
snippets I use in everyday work.
Basic Introduction to Snippets
A code snippet is just a chunk of code that you can enter quickly because
typing it by hand for the Nth time can be rather boring. Here is an
example of one such entry:
#region INotifyPropertyChanged Members
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
To get the above code to appear in VS, I just type a magic combination of letters (npc
, in this case),
press Tab, and
the above block of code is injected at the insertion point.
Some snippets allow you to customize them, i.e., edit parts of them after the snippet has been injected. To allow for this,
VS will show placeholders for variables that can be edited. The user can then use
the Tab character to move from one placeholder to another. Here’s how it
typically looks:
Generative Snippets
The above mechanism, as I’m sure you’ll agree, is not very powerful. I mean, it’s useful for tiny little things that you can inject, but the problem is that you cannot execute C# inside the snippet – in fact, Visual Studio does provide 3 functions that you can execute, but these functions are of little use to us.
What I wanted to do with snippets is make them parameterized. For example, I want to type in entity3
and get a class with 3 auto-properties. After thinking about it long enough, I decided that the only way to get this to work is to generate cases exhaustively. For example, for the entity class I might need one with between 2 and 20 members. So, in a single snippet file, I generate all cases
individually. This may sound like hard work, and it is, so before we go to the
showcase, I’d like to present some C# code on how snippets are generated. This
is only useful if you plan on generating snippets of your own: if not, feel free
to skip to the Examples section.
How Is It Done?
The API for generating snippets is really simple. In fact, the following diagram pretty much sums it up:
Code snippets are defined in XML, and the above classes are, basically, objects that help generating this XML a little easier. On the top level, we have the SnippetCollection
that appears once per file. It aggregates a number of Snippet
object, which define all possible iterations of our generated snippets (e.g., entity1
... entity10
). In addition to the snippets themselves, the user-editable parameters are defined in SnippetLiteral
objects that make part of a SnippetLiteralCollection
.
Here's a short guide on how to write your own snippets. First, we define the snippet collection:
var sc = new SnippetCollection();
Then, we put as many iterative loops as we require for our snippets. I'll just use one, with a counter from 1 to 10. Inside the loop, we create the Snippet
object and set its properties (most of them are mandatory, so I don't recommend skipping any.
for (int i = 2; i < count; ++i)
{
var s = new Snippet
{
Author = author,
Description = "Creates an inline multiplication equivalent to Math.Pow(…, " + i + ").",
Shortcut = "pow" + i,
Title = "pow" + i
};
Literals can be added by explicitly instantiating the SnippetLiteral
objects, but there also helper methods in the associated collection class. Let's add a literal to our snippet:
s.Literals.AddLiteral("x", "Variable name");
Now that we've added our literal, we can use it by typing $x$
in the body of the snippet. To create the body (which turns into a CDATA block in the snippet itself), we get the StringBuilder
from the snippet and use it. Here's how it's done:
var sb = s.CodeBuilder;
for (int j = 1; j <= i; ++j)
{
sb.Append("$x$");
if (j != i)
sb.Append("*");
}
Now, before exiting the loop, we add the snippet to the snippet collection.
sc.Add(s);
Finally, once we are done with all the loops, we save the snippet collection itself.
sc.Save("pow");
You might need to tweak the Save
method to save to the location of
your choice, but apart from that, the API presented here can be used without
modification, and will output syntactically correct snippet files.
Showcase
Presented below are examples of some of the generative snippets included with the source code. Please note that some examples produce far too much code to be shown here, so I'll provide a textual description instead.
arglistX
Creates a list of comma-separated variables with a common name followed by a 1-based index:
arrayX
Creates a declaration of an array with X elements. All elements are initialized to the same value
array5 |
double[] d = { 1.0, 1.0, 1.0, 1.0, 1.0 }; |
arrayXbyY
Creates a declaration of a 2D array with X×Y elements. All elements are initialized to the same value, but for square arrays, the diagonal can be initialized separately. Note also that code for these snippets will not be reformatted correctly by Visual Studio.
array4by7 |
float[,] f = {
{ 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f },
{ 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f },
{ 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f },
{ 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f }
}; |
array8by8 |
double[,] i = {
{ 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
{ 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
{ 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
}; |
forCX
Creates X inset for
loops with the outer indexer starting at the letter C.
fora5 |
for (int a = 0; a < 10; ++a)
{
for (int b = 0; b < 20; ++b)
{
for (int c = 0; c < 30; ++c)
{
for (int d = 0; d < 40; ++d)
{
for (int e = 0; e < 77; ++e)
{
}
}
}
}
} |
parrX
Adds a code stub to run X pieces of code in parallel. Uses AutoResetEvent
. Note that in Parallel Extensions, we have Parallel.Invoke()
for this.
parr3 |
AutoResetEvent are1 = new AutoResetEvent(false);
AutoResetEvent are2 = new AutoResetEvent(false);
AutoResetEvent are3 = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem(delegate
{
are1.Set();
});
ThreadPool.QueueUserWorkItem(delegate
{
are2.Set();
});
ThreadPool.QueueUserWorkItem(delegate
{
are3.Set();
});
WaitHandle.WaitAll(new WaitHandle[] { are1, are2, are3 }); |
catchX
Adds code to catch X different types of exception. Probably the most boring snippet of them all.
catch3 |
catch (Exception e)
{
}
catch (Exception e)
{
}
catch (Exception e)
{
} |
flagsX
Creates a [Flags]
-tagged enumeration with X members. Enum name, member names and comments are editable.
Enum type depends on how many elements you want to have. Also generates None
and All
members, which are sometimes useful.
flags3 |
[System.Flags]
enum EnumName : byte
{
None = 0,
Element1 = 1,
Element2 = 2,
Element3 = 4,
All = 7
}
|
getflagsX
Tests for X flags in an enum, and creates X boolean variables.
getflags4 |
bool isPrivate = ((modifiers & Private) == Private);
bool isProtected = ((modifiers & Protected) == Protected);
bool isInternal = ((modifiers & Internal) == Internal);
bool isPublic = ((modifiers & Public) == Public);
|
nulltestX
Test a chain of X properties for null
. Chain members are, of
course, editable. This snippet is best illustrated in code.
nulltest4
|
if (a != null &&
a.Props != null &&
a.Props.Members != null &&
a.Props.Members.X != null)
{
}
|
powX
Inlines a power calculation instead of using Math.Pow()
. Takes a specified term to the power of X. This is the example I showed in the previous section.
polyX & polyPX
These two snippet sets both manufacture member functions that compute a polynomial with highest degree X. The difference is that polyX
does the calculation using inline multiplication (similar to how powX
outputs it) whereas polyPX
uses Math.Pow()
. The difference in execution speeds is quite dramatic!
poly4 |
public double Poly(double x, double a, double b, double c, double d, double e)
{
return a * x * x * x * x + b * x * x * x + c * x * x + d * x + e;
}
|
polyP4 |
public double PolyP(double x, double a, double b, double c, double d, double e)
{
return a * Math.Pow(x, 4) + b * Math.Pow(x, 3) + c * Math.Pow(x, 2) + d * x + e;
}
|
varlistX
Declares X variables (variable names starting with ‘a’) in a single line of code.
varlist5 |
double a = 0, b = 0, c = 0, d = 0, e = 0; |
fsmX
Creates a finite state machine with X states. This includes declaration of the fsm enum, creation of Before- and After- EventArgs
classes, and the creation of the state machine itself; many elements are optional and can be safely removed. The state machine is quite verbose, so I won't present an example here. To try this snippet, just download the code.
subX & supX
Toy snippets that create subscript and superscript characters. They work in the Consolas font. The main purpose is being able to avoid opening the Character Map while trying to embellish your comments with clever little
sub/superscript symbols. A demo cannot be presented here – try it out in Visual Studio.
Entity snippets
Entity snippets create ready-made entity classes. There are several types, with different levels of infrastructure support. Here is a short list of the ones we have so far:
- tupleXsimple creates a tuple class with X elements.
- entityXauto creates a class with X auto-properties.
- arrstoreX generates an array-based storage class with X elements.
- dpentityXbyY creates a
DependencyProperty
-based entity class with X read-write properties and Y read-only properties.
- entityXslim creates a class with X properties whose read and write behavior is regulated with
ReaderWriterLockSlim
. Note: requires .NET 3.5.
Conclusion
Generating snippets is one of the many ways in which one can easily create parameterized code generation. Though the results achieved are fairly simple,
there are situations where this amount of flexibility is sufficient to get the job done.
So if this article got you interested, check out the snippets (and the source
code) and let me know what you think.
You can leave comments here or on the CodePlex project page.