Lee Swainsbury
Lee Swainsbury

Reputation: 375

String interpolation using named parameters in C#6

Given I have a Meta Data resource string stored in my Database that will return like this:

var pageTitle = "Shop the latest {category1} Designer {category2} {category3} at www.abc.com";

And I want to replace the {placeholders} with variable values;

var category1 = "womenswear";
var category2 = "dresses";
var category3 = "cocktail dresses";

I have tried, with no luck;

var newTitle = $"pageTitle, category1, category2, category3";
var newTitle = $(pageTitle, category1, category2, category3);

I know that I could use string.Replace() that has a performance overhead. Does anyone know how this can be done efficiently using the latest C# string interpolation?

Upvotes: 20

Views: 8742

Answers (5)

wendellmva
wendellmva

Reputation: 1944

Please checkout my libary thats solves this problem TinyTools if you use it give me an upvote and if you like it leave a star on github.

    var person = new Person
    {
        FirstName = "John",
        LastName = "Smith"
    };
    var template = "Hello world, I'm {FirstName} {LastName}";

    var result = template.Interpolate(person);
    result.ShouldBe("Hello world, I'm John Smith");

Upvotes: 1

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726849

Although .NET does not offer a built-in capability for this, you can easily build a pretty good implementation with regex:

public static string Format(string formatWithNames, IDictionary<string,object> data) {
    int pos = 0;
    var args = new List<object>();
    var fmt = Regex.Replace(
        formatWithNames
    ,   @"(?<={)[^}]+(?=})"
    ,   new MatchEvaluator(m => {
            var res = (pos++).ToString();
            var tok = m.Groups[0].Value.Split(':');
            args.Add(data[tok[0]]);
            return tok.Length == 2 ? res+":"+tok[1] : res;
        })
    );
    return string.Format(fmt, args.ToArray());
}

The idea is to replace names with consecutive numbers (see sb.Append(pos++)), build an array of object parameters (see args.Add(data[tok[0]])), and pass the result to string.Format to use string's formatting capabilities.

Now you can pass your resource to this Format, along with a dictionary of key-value pairs, and get a string formatted using .NET's built-in formatting capabilities:

var x = new Dictionary<string,object> {
    {"brown", 1}
,   {"jumps", 21}
,   {"lazy", 42}
};
Console.WriteLine("{0}", Format("Quick {brown} fox {jumps:C} over the {lazy:P} dog", x));

Note that there is no $ in front of the string literal, so there's no C#'s built-in interpolation going on here.

This prints

Quick 1 fox $21.00 over the 4,200.00 % dog

The approach is based on the idea by Scott Hanselman, except formatting is done differently.

Demo.

Upvotes: 11

D Stanley
D Stanley

Reputation: 152626

If you don't want to use string replacement, then you either need to use the original formatting syntax:

var pageTitle = "Shop the latest {0} Designer {1} {2} at www.abc.com";

var category1 = "womenswear";
var category2 = "dresses";
var category3 = "cocktail dresses";

var newTitle = string.Format(pageTitle, category1, category2, category3);

or put the formatting string after the variables:

var category1 = "womenswear";
var category2 = "dresses";
var category3 = "cocktail dresses";

var newTitle = $"Shop the latest {0} Designer {0} {0} at www.abc.com";

The string interpolation is converted to a string.Format call at compile time, so there's no way to make the formatting string a variable at run-time.

Upvotes: 5

CodeCaster
CodeCaster

Reputation: 151674

I know that I could use string.Replace() that has a performance overhead

Of mere nanoseconds. You shouldn't optimize prematurely.

As said, the C# 6 string interpolation feature happens at compile-time. If you want to do this at runtime, you'll have to use string replacement.

I assume your strings are formatted like that for ease of editing and not having a fixed replacement order, so simply do this:

var input = "Shop the latest {category1} Designer {category2} {category3} at www.abc.com";
var category1 = "womenswear";
var category2 = "dresses";
var category3 = "cocktail dresses";

var result = input.Replace("{category1}", category1)
                  .Replace("{category2}", category2)
                  .Replace("{category3}", category3);

This will just work. You could also store your replacement values in a dictionary, and loop over the keys to replace the key with the value.

Upvotes: 18

Patrick Hofman
Patrick Hofman

Reputation: 157048

You can't use string interpolation here. String interpolation is a compile-time rewrite method for string.Format, which is the solution you should use:

var newTitle = string.Format("{0}, {1}, {2}, {3}", pageTitle, category1, category2, category3);

Or string.Join:

var newTitle - string.Join(", ", new string[] { pageTitle, category1, category2, category3 });

Since you are loading from database, it might be just better in your case to keep using string.Replace.

Upvotes: 20

Related Questions