C# Named parameters to a string that replace to the parameter values

18,859

Solution 1

Have you confirmed that regular expressions are too expensive?

The cost of regular expressions is greatly exaggerated. For such a simple pattern performance will be quite good, probably only slightly less good than direct search-and-replace, in fact. Also, have you experimented with the Compiled flag when constructing the regular expression?

That said, can't you just use the simplest way, i.e. Replace?

string varname = "name";
string pattern = "{" + varname + "}";
Console.WriteLine("Hi {name}".Replace(pattern, "Mike"));

Solution 2

Regex is certainly a viable option, especially with a MatchEvaluator:

    Regex re = new Regex(@"\{(\w*?)\}", RegexOptions.Compiled); // store this...

    string input = "Hi {name}, do you like {food}?";

    Dictionary<string, string> vals = new Dictionary<string, string>();
    vals.Add("name", "Fred");
    vals.Add("food", "milk");

    string q = re.Replace(input, delegate(Match match)
    {
        string key = match.Groups[1].Value;
        return vals[key];
    });

Solution 3

Now if you have you replacements in a dictionary, like this:

    var  replacements = new Dictionary<string, string>();
    replacements["name"] = "Mike";
    replacements["age"]= "20";

then the Regex becomes quite simple:

Regex regex = new Regex(@"\{(?<key>\w+)\}");
    string formattext = "{name} is {age} years old";
    string newStr = regex.Replace(formattext, 
            match=>replacements[match.Groups[1].Captures[0].Value]);

Solution 4

After thinking about this, I realized what I actually wished for, was that String.Format() would take an IDictionary as argument, and that templates could be written using names instead of indexes.

For string substitutions with lots of possible keys/values, the index numbers result in illegible string templates - and in some cases, you may not even know which items are going to have what number, so I came up with the following extension:

https://gist.github.com/896724

Basically this lets you use string templates with names instead of numbers, and a dictionary instead of an array, and lets you have all the other good features of String.Format(), allowing the use of a custom IFormatProvider, if needed, and allowing the use of all the usual formatting syntax - precision, length, etc.

The example provided in the reference material for String.Format is a great example of how templates with many numbered items become completely illegible - porting that example to use this new extension method, you get something like this:

var replacements = new Dictionary<String, object>()
                       {
                           { "date1", new DateTime(2009, 7, 1) },
                           { "hiTime", new TimeSpan(14, 17, 32) },
                           { "hiTemp", 62.1m },
                           { "loTime", new TimeSpan(3, 16, 10) },
                           { "loTemp", 54.8m }
                       };

var template =
    "Temperature on {date1:d}:\n{hiTime,11}: {hiTemp} degrees (hi)\n{loTime,11}: {loTemp} degrees (lo)";

var result = template.Subtitute(replacements);

As someone pointed out, if what you're writing needs to be highly optimized, don't use something like this - if you have to format millions of strings this way, in a loop, the memory and performance overhead could be significant.

On the other hand, if you're concerned about writing legible, maintainable code - and if you're doing, say, a bunch of database operations, in the grand scheme of things, this function will not add any significant overhead.

...

For convenience, I did attempt to add a method that would accept an anonymous object instead of a dictionary:

public static String Substitute(this String template, object obj)
{
    return Substitute(
        template,
        obj.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(obj, null))
    );
}

For some reason, this doesn't work - passing an anonymous object like new { name: "value" } to that extension method gives a compile-time error message saying the best match was the IDictionary version of that method. Not sure how to fix that. (anyone?)

Solution 5

How about

stringVar = "Hello, {0}. How are you doing?";
arg1 = "John";    // or args[0]
String.Format(stringVar, arg1)

You can even have multiple args, just increment the {x} and add another parameter to the Format() method. Not sure the different but both "string" and "String" have this method.

Share:
18,859
Admin
Author by

Admin

Updated on July 26, 2022

Comments

  • Admin
    Admin almost 2 years

    I want in a good performance way (I hope) replace a named parameter in my string to a named parameter from code, example, my string:

    "Hi {name}, do you like milk?"
    

    How could I replace the {name} by code, Regular expressions? To expensive? Which way do you recommend?

    How do they in example NHibernates HQL to replace :my_param to the user defined value? Or in ASP.NET (MVC) Routing that I like better, "{controller}/{action}", new { controller = "Hello", ... }?

  • Jon Skeet
    Jon Skeet over 15 years
    (Although you mean Replace(pattern, "Mike"))
  • James Curran
    James Curran over 15 years
    Damn... How'd you get that 18 minutes before me?
  • steve_c
    steve_c over 15 years
    If you're on .NET 3.5, you can kill the delegate keyword. delegate(Match match) can be match =>
  • Konrad Rudolph
    Konrad Rudolph over 15 years
    @Jon: Thanks. Why use a compiler when you've got a Jon? ;-)
  • Marc Gravell
    Marc Gravell over 15 years
    @scalvert - to be exact, that is C# 3.0, not .NET 3.5; it would work targetting .NET 2.0 with C# 3.0, too.
  • mindplay.dk
    mindplay.dk about 13 years
    +1 for brevity - of all the contributed solutions, this is my personal favorite :-)
  • Brady Moritz
    Brady Moritz over 11 years
    he needs named parameters, not ordered ones.
  • Brady Moritz
    Brady Moritz over 11 years
    hate regexes, but this is pretty nice.
  • Brady Moritz
    Brady Moritz over 11 years
    did you ever figure out the anonymous type for this? that would be really cool.
  • mindplay.dk
    mindplay.dk over 11 years
    I don't recall - that was a long time ago, and I don't currently work with ASP.NET... I looked at the code from back then, and it went into production looking the same - I vaguely recall the problem being somehow not with this code, but with the consumer-code. That's the best clue I can give you.