March 2008 - Posts
The problem with string formatting is that {0}, {1}, {2}, etc, aren't very descriptive. Figuring out what the numbers represent means either deducing their values from their usage context in the string or examining the arguments passed to String.Format. Things become more difficult when you are working with a format string stored in an external resource as the second option no longer available.
FormatWith 2.0
I have updated the original FormatWith with an overload that takes a single argument and allows the use of property names in the format string.
MembershipUser user = Membership.GetUser();
Status.Text = "{UserName} last logged in at {LastLoginDate}".FormatWith(user);
It also works with anonymous types:
"{CurrentTime} - {ProcessName}".FormatWith(new { CurrentTime = DateTime.Now, ProcessName = p.ProcessName });
And even allows for sub-properties and indexes:
var student = new
{ Name = "John",
Email = "john@roffle.edu",
BirthDate = new DateTime(1983, 3, 20),
Results = new[]
{ new { Name = "COMP101", Grade = 10 }, new { Name = "ECON101", Grade = 9 } }
};
Console.WriteLine("Top result for {Name} was {Results[0].Name}".FormatWith(student));// "Top result for John was COMP101"
There isn't much to the code itself. Most of the heavy lifting is done in a regular expression and the .NET DataBinder class.
To start with a regular expression picks all the {Property} blocks out of the string. The the property expression inside the brackets is then evaluated using the DataBinder class on the object argument to get the property value. That value is then put into a list of values and the {Property} block is replaced in the string with the index of the value in the list, e.g. {1}. Finally the rewritten string, which now looks like any other format string, and the list of evaluated values are passed to String.Format.
public static string FormatWith(this string format, object source)
{ return FormatWith(format, null, source);
}
public static string FormatWith(this string format, IFormatProvider provider, object source)
{ if (format == null)
throw new ArgumentNullException("format");
Regex r = new Regex(@"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
List<object> values = new List<object>();
string rewrittenFormat = r.Replace(format, delegate(Match m)
{ Group startGroup = m.Groups["start"];
Group propertyGroup = m.Groups["property"];
Group formatGroup = m.Groups["format"];
Group endGroup = m.Groups["end"];
values.Add((propertyGroup.Value == "0")
? source
: DataBinder.Eval(source, propertyGroup.Value));
return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value + new string('}', endGroup.Captures.Count); });
return string.Format(provider, rewrittenFormat, values.ToArray());
}

I have a love/hate relationship with String.Format.
On one hand it makes strings easier to read and they can easily be put in an external resource.
On the other hand every bloody time I want to use String.Format, I only realize when I am already halfway through writing the string. I find jumping back to the start to add String.Format is extremely annoying and that it breaks me out of my current train of thought.
FormatWith
FormatWith is an extension method that wraps String.Format. It is the first extension method I wrote and is probably the one I have used the most.
Logger.Write("CheckAccess result for {0} with privilege {1} is {2}.".FormatWith(userId, privilege, result));
An extension method with the same name as a static method on the class causes the C# compiler to get confused, which is the reason why I have called the method FormatWith rather than Format.
public static string FormatWith(this string format, params object[] args)
{ if (format == null)
throw new ArgumentNullException("format");
return string.Format(format, args);
}
public static string FormatWith(this string format, IFormatProvider provider, params object[] args)
{ if (format == null)
throw new ArgumentNullException("format");
return string.Format(provider, format, args);
}

Update:
You may also be interested in FormatWith 2.0.
If you regularly visit my blog (subscribe already!) you may have noticed the design has been updated.
Here is a comparison between the old and the new. Click for a bigger image:
I threw out the old design and started with a template called EliteCircle, which I have made some fairly substantial updates to. I was a little hesitant about changing the CSS too much, the last time I had done any serious CSS work was over two years ago, but everything ending up going smoothly.
Changes
The previous look was simple which I liked, but it was gradually getting overrun by tag and archive links. Tags are now displayed in a screen real estate efficient cloud, and archive links have been moved to a separate page.
The design is slightly wider. The previous design's minimum resolution was 800x600, which according to Google Analytics is used by just 0.3% of visitors these days. The new minimum resolution is now 1024x768. With the wider design I have increased the content text to match.
In the new design I have also removed a lot of what I think is unnecessary information from the blog. The focus of a blog should be the content. Details like what categories a post is in or exactly what time a comment was made aren't of interested to the average visitor and have been removed to reduce screen clutter.
Overall I am really happy with the new design and I think it is a big improvement over what was here before.
Obligatory
As always with a redesign, if you spot anything weird or broken, or just think something looks bad, let me know. Thanks!
Intergenite Andrew Tokeley recently blogged about how a Microsoft developer has to know so much more to be considered a senior dev (or even intermediate!) compared to just 5 years ago.
This graph of the number .NET types in the framework made by Brad Abrams illustrates the point pretty well:
I started .NET development in 2003 with .NET 1.1. Fast forward to 2008 and the number of types in the .NET framework has tripled!
Remembering how overwhelmed I felt when I first approached .NET back in 2003, I can't imagine how a junior developer today must feel.

Thanks to everyone who came along to the Wellington Dot User Group meeting this evening for my presentation on Silverlight 2.0. We were close to standing room only, which is an indicator of the great interested within the local .NET community around Silverlight.
If you are interested in developing with Silverlight, then http://www.silverlight.net is the one stop shop to help you get started. Silverlight.net is the official Microsoft Silverlight website and it has the Silverlight runtime, tools, examples and an active community if you are in need of assistance.
Feedback on the presentation has been great and I hope everyone who attended feels ready to jump into the world of Silverlight development!
Download the slides here.
More Posts
Next page »