c# 6 introduced string interpolation syntax. This feature allows for formatted text to be easily declared in code. For example:
var name = "World";
var message = $"Hello, {name}"; // "Hello, World"
At it's most basic, this is syntactic sugar on top of c#'s string.Format
method, but with a much cleaner syntax. However, simply interpolating strings is often a dangerous proposition. Consider the following:
var url = $"https://api.example.com/sample?arg={arg}";
This will work in most cases, but if the parameter includes non-url-safe characters, this is likely to break. This could even expose a serious security vulnerability, expecially if you're doing string interpolation for HTML, javascript, or SQL (don't ever do this!).
Formattable String
Hidden deep in the c# language spec is a minor note about string interpolation. In general, interpolated strings are compiled to a call to string.Format
, but you can also cast it to a FormattableString
. This type represents the string template and an array of objects which will be interpolated into it.
var make = "Chrysler";
var model = "Town & Country";
var url = (FormattableString)$"https://api.example.com/vehicles?make={make}&model={model}";
Console.WriteLine(url.Format); // "https://api.example.com/vehicles?make={0}&model={1}";
Console.WriteLine(url.GetArgument(0)); // "Chrysler";
Console.WriteLine(url.GetArgument(1)); // "Town & Country";
This provides some interesting opportunities to create much more powerful string templating tools. For example, if we wanted to automatically encode the URL arguments, we can do the following:
public static class Format
{
public static Uri Uri(FormattableString template)
{
var encodedArgs = new object[template.ArgumentCount];
for (var i = 0; i < template.ArgumentCount; i++)
{
var original = template.GetArgument(i);
encodedArgs[i] = HttpUtility.UrlEncode(original);
}
return new Uri(string.Format(template.Format, encodedArgs));
}
}
The above code creates a new array and populates it with the url-encoded arguments. It then calls string.Format
with the original template and new encoded arguments and returns it as a Uri to indicate that it's been safely encoded.
to use it, we can call
var make = "Chrysler";
var model = "Town & Country";
var url = Format.Uri($"https://api.example.com/vehicles?make={make}&model={model}");
Console.WriteLine(url); // https://api.example.com/vehicles?make=Chrysler&model=Town+%26+Country
Custom Formats
Another interesting feature we can make (ab)use of is custom format strings. In a traditional c# string template, you can specify a format for each argument, I.E.
Console.WriteLine($"Today is {DateTime.Now:yyyy-MM-dd}"); // Today is 2019-12-13
This is effectively the equivelant of calling dateTime.ToString("yyyy-MM-dd")
. Any object that implements IFormattable
can be used with a custom format string, which gives us an opportunity to define a simple syntax when working with string templates. In this example, we'll set up a simple HTML template that will either html encode a value or format it as markdown.
public static HtmlString Html(FormattableString template)
{
var encodedArgs = new object[template.ArgumentCount];
for (var i = 0; i < template.ArgumentCount; i++)
{
encodedArgs[i] = new HtmlArgument(template.GetArgument(i));
}
return new HtmlString(string.Format(template.Format, encodedArgs));
}
class HtmlArgument : IFormattable
{
public HtmlArgument(object value)
{
Value = value;
}
public object Value { get; }
public string ToString(string format, IFormatProvider formatProvider)
{
switch (format)
{
case "markdown":
return new Markdown().Transform(Value.ToString());
case "dangerous-raw-html":
return Value.ToString();
default:
return HttpUtility.HtmlEncode(Value);
}
}
}
We can then use this as follows:
var html = Format.Html($"<article><h1>{title}</h1>{content:markdown}</article>");
title
will be safely HTML encoded, and content
will be rendered as markdown.
Wrapping Up
String interpolation in c# is convenient, but can lead to some traps. If not used carefully, it can break with edge cases or even introduce vulnerabilities. Formattable strings are a little known, but potentially quite useful feature in c# that can be used to make string interpolation smarter and more context-aware.
Top comments (4)
Wow! I learned something new today! Thanks for sharing
Actually, interpolated strings was introduced in c# 6, not 7. However, I didn't know about FormattableStrings, so thanks!
Totally right. Fixed.
Thanks for your this great article!