3

I have a dictionary with key value pair, and I need to use String Interpolation and then map the string according to the named parameters. Please help me.

// code removed for brevity
var rawmessage = "Hello my name is {Name}"; // raw message
var dict= new Dictionary<string, object>();
dict.Add("{Name}", response.Name);

I want to be able to do something like:

// Pseudo code
$"Hello my name is {Key}", dict;

Solution I have found till now:

     var message = parameters.Aggregate(message, 
(current, parameter) => current.Replace(parameter.Key, parameter.Value.ToString()));

And it works fine, but my manager really wants me to use String Interpolation with Dictionary. I am not sure how to achieve this. Please guide me.

Unbreakable
  • 7,776
  • 24
  • 90
  • 171
  • String interpolation is just syntactic sugar around `string.Format`, you can't use preset strings like this. – DavidG Sep 11 '18 at 16:54
  • @DavidG If you have a dictionary with key value and a string with named parameter, how would you map it then? – Unbreakable Sep 11 '18 at 16:57
  • Your last bit of code is really the only way. Though if you have lots of replacements to do, you may want to consider using a `StringBuilder` for performance. – DavidG Sep 11 '18 at 16:58
  • Now, why a negative vote! Seriously! – Unbreakable Sep 11 '18 at 17:04
  • Not from me, it's a valid question, just not possible to achieve. – DavidG Sep 11 '18 at 17:04
  • Could you explain what do you mean by using using string interpolation? I would like to see better example – sTrenat Sep 11 '18 at 17:07
  • @sTrenat: So I have a dictionary with suppose 5 name value pair, and I want to use String Interpolation to map the named parameters (which are present in the dictionary as Keys) to their respective values. But I don't want to use `String.Replace`. Is there any way out? – Unbreakable Sep 11 '18 at 17:11
  • A library like dotLiquid can handle scenarios like this. – AaronLS May 06 '20 at 00:19

3 Answers3

10

The problem is that string interpolation in C# is just a syntactic sugar wrapper for string.Format. The strings get compiled to the same code. Take this code for example:

public string GetGreeting1(string name)
{
    return string.Format("Hello {0}!", name);
}

public string GetGreeting2(string name)
{
    return $"Hello {name}!";
}

The IL output for both methods is identical:

GetGreeting1:
IL_0000:  ldstr       "Hello {0}!"
IL_0005:  ldarg.1     
IL_0006:  call        System.String.Format
IL_000B:  ret         

GetGreeting2:
IL_0000:  ldstr       "Hello {0}!"
IL_0005:  ldarg.1     
IL_0006:  call        System.String.Format
IL_000B:  ret  

So the solution you have is perfectly valid and one I've used before. The only possible improvement would be if you intend on doing lots of replacements, you may find a performance benefit if you switch to using a StringBuilder. You could probably find a library on Nuget that does it for you, but that might just be overkill for your purpose.

As an extra, I made an extension class for this. With an extra bonus I've also added a method for using an object with any number of parameters that uses reflection to do the replacements:

public static class StringReplacementExtensions
{
    public static string Replace(this string source, Dictionary<string, string> values)
    {
        return values.Aggregate(
            source,
            (current, parameter) => current
                .Replace($"{{{parameter.Key}}}", parameter.Value));
    }

    public static string Replace(this string source, object values)
    {
        var properties = values.GetType().GetProperties();

        foreach (var property in properties)
        {
            source = source.Replace(
                $"{{{property.Name}}}", 
                property.GetValue(values).ToString());
        }

        return source;
    }
}

And use like this:

var source = "Hello {name}!";

//Using Dictionary:
var dict = new Dictionary<string, string> { { "name", "David" } };
var greeting1 = source.Replace(dict);
Console.WriteLine(greeting1);

//Using an anonymous object:
var greeting2 = source.Replace(new { name = "David" });
Console.WriteLine(greeting2);
DavidG
  • 113,891
  • 12
  • 217
  • 223
7

And it works fine, but my manager really wants me to use String Interpolation with Dictionary. I am not sure how to achieve this.

You can't do that. C# interpolated string is not a templating engine but a compile-time feature, which translates to String.Format calls. That's why you cannot use string interpolation in constant expressions either.

I want to use String Interpolation to map the named parameters (which are present in the dictionary as Keys) to their respective values. But I don't want to use String.Replace. Is there any way out?

Actually there are several templating engines you can use (mainly for logging), so you don't need to reinvent the wheel. You might find Serilog interesting, for example.

See also this thread: What's a good way of doing string templating in .NET?

György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
1

The above answers are not quite correct in claiming that interpolated strings are simply syntactic sugar for string.Format(), they are capable of just a little bit more. This makes it possible to accomplish what you desire with FormattableString and a custom IFormatProvider/ICustomFormatter, like so:

public class DictionaryFormatter : IFormatProvider, ICustomFormatter
{
    private Dictionary<string, string> _dict;
    public DictionaryFormatter(Dictionary<string, string> dict)
    {
        _dict = dict;
    }
    public string Format(string? format, object? arg, IFormatProvider? formatProvider)
    {
        var key = arg?.ToString() ?? String.Empty;
        return _dict[key];
    }

    public object? GetFormat(Type? formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }
}

Usage:

FormattableString x = $"This is an interpolated string with the first argument being: {"One"} and the second being {"Two"}";
var dict = new Dictionary<string, string>() {{"One", "First Value"}, {"Two", "Second Value"}};
var result = x.ToString(new DictionaryFormatter(dict));
Console.WriteLine(x.Format);
Console.WriteLine(string.Join(',', x.GetArguments()));
Console.WriteLine(result);

Output:

"This is an interpolated string with the first argument being {0} and the second being {1}"
"One,Two"
"This is an interpolated string with the first argument being First Value and the second being Second Value"
Taedrin
  • 445
  • 1
  • 4
  • 16