18

Can one store the template of a string in a variable and use interpolation on it?

var name = "Joe";
var template = "Hi {name}";

I then want to do something like:

var result = $template;

The reason is my templates will come from a database.

1
  • 3
    it's not clear what you are asking. can you clarify what values are coming from database and what is your expected output ? Commented Apr 8, 2017 at 0:25

5 Answers 5

14

I guess that these strings will have always the same number of parameters, even if they can change. For example, today template is "Hi {name}", and tomorrow could be "Hello {name}".

Short answer: No, you cannot do what you have proposed.

Alternative 1: use the string.Format method.

You can store in your database something like this:

"Hi {0}"

Then, when you retrieve the string template from the db, you can write:

var template = "Hi {0}"; //retrieved from db
var name = "Joe";
var result = string.Format(template, name);
//now result is "Hi Joe"

With 2 parameters:

var name2a = "Mike";
var name2b = "John";
var template2 = "Hi {0} and {1}!"; //retrieved from db
var result2 = string.Format(template2, name2a, name2b);
//now result2 is "Hi Mike and John!"

Alternative 2: use a placeholder.

You can store in your database something like this:

"Hi {name}"

Then, when you retrieve the string template from the db, you can write:

var template = "Hi {name}"; //retrieved from db
var name = "Joe";
var result = template.Replace("{name}", name);
//now result is "Hi Joe"

With 3 parameters:

var name2a = "Mike";
var name2b = "John";
var template2 = "Hi {name2a} and {name2b}!"; //retrieved from db
var result2 = template2
    .Replace("{name2a}", name2a)
    .Replace("{name2b}", name2b);
//now result2 is "Hi Mike and John!"

Pay attention at which token you choose for your placeholders. Here I used surrounding curly brackets {}. You should find something that is unlikely to cause collisions with the rest of your text. And that depends entirely on your context.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer. I used string.Format for years and string interpolation has made the code much more readable. I was hoping it would work with a template variable as string.Format gets really ugly and error prone when you get upwards of 20 variables. I'll likely go with string.Replace for each variable and substitute the template placeholders myself.
@TonyLugg what's the advantage of using string.Replace rather than string.Format? How would you use it, I'm curious about that.
Sorry for the late reply. I have many variables that need to be replaced in a very large document. Using string.Format would be really hard to read and error prone (order has to match the {#} instances in the source string. Replace self-documents which variable is being replaced by which replacement string.
10

This can be done as requested using dynamic compilation, such as through the Microsoft.CodeAnalysis.CSharp.Scripting package. For example:

var name = "Joe";
var template = "Hi {name}";
var result = await CSharpScript.EvaluateAsync<string>(
    "var name = \"" + name + "\"; " +
    "return $\"" + template + "\";");

Note that this approach is slow, and you'd need to add more logic to handle escaping of quotes (and injection attacks) within strings, but the above serves as a proof-of-concept.

4 Comments

super hack but legit and I like it ;)
Be careful to match the version of .NET you're using in your project to the version of this new compiler package. See here: github.com/dotnet/roslyn/wiki/NuGet-packages
Oh, one other follow-up: Any need to do anything to make your code thread safe when using this approach?
@Wade: The EvaluateAsync receives an immutable string as its argument, so it is impervious to any subsequent changes to the original variables. The code is thread-safe as is.
3

I just had the same need in my app so will share my solution using String.Replace(). If you're able to use LINQ then you can use the Aggregate method (which is a reducing function, if you're familiar with functional programming) combined with a Dictionary that provides the substitutions you want.

string template = "Hi, {name} {surname}";

Dictionary<string, string> substitutions = new Dictionary<string, string>() {
    { "name", "Joe" },
    { "surname", "Bloggs" },
};

string result = substitutions.Aggregate(template, (args, pair) =>
    args.Replace($"{{{pair.Key}}}", pair.Value)
);
// result == "Hi, Joe Bloggs"

This works by starting with the template and then iterating over each item in the substitution dictionary, replacing the occurrences of each one. The result of one Replace() call is fed into the input to the next, until all substitutions are performed.

The {{{pair.Key}}} bit is just to escape the { and } used to find a placeholder.

2 Comments

I love this solution. Allows for adding additional placeholders easily, and avoids the looping.
thanks, .NET Fiddle dotnetfiddle.net/zgvXDt
2

No you can't do that since it needs name value at the time string is created (compile time). Consider using String.Format or String.Replace instead.

1 Comment

1

This is pretty old now, but as I've just come across it it's new to me!

It's a bit overkill for what you need, but I have used Handlebars.NET for this sort of thing.

You can create quite complex templates and merge in hierarchical data structures for the context. There's rules for looping and conditional sections, partial template compositing and even helper function extension points. It also handles many data types gracefully.

There's way too much to go into here, but a short example to illustrate...

    var source = @"Hello {{Guest.FirstName}}{{#if Guest.Surname}} {{Guest.Surname}}{{/if}}!";
    var template = Handlebars.Compile(source);

    var rec = new {
        Guest = new { FirstName = "Bob", Surname = null }
    };

    var resultString = template(rec);

In this case the surname will only be included in the output if the value is not null or empty.

Now admittedly this is more complicated for users than simple string interpolation, but remember that you can still just use {{fieldName}} if you want to, just that you can do lots more as well.

This particular nuGet is a port of HandlebarsJs so it has a high degree of compatibility. HandlebarsJs is itself a port of Mustache - there are direct dotNet ports of Mustache but IMHO HandlebarsNET is the business.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.