C# String Interpolation on <appSettings>

Viewed 5534

I am liking the new Interpolation syntax of C#.

I want to store a dynamic string in my .config file under appSettings and apply interpolation on it. The expected value of interpolatedMessage is "Your User Name is SnowWhite"

It would be great if this is possible. This will help me to keep the key to be configurable to any combination. (e.g. "SnowWhite is your User Name", "User Name - SnowWhite")

 <appSettings>
    <add key="userNameKey" value="Your User Name is {userName}" />
  </appSettings>

enter image description here

6 Answers

I also missed the behavior of more readable strings in config. So I create an extension method on string.

public static class StringExtensions
{
    public static string Interpolate(this string self, object interpolationContext)
    {
        var placeholders = Regex.Matches(self, @"\{(.*?)\}");
        foreach (Match placeholder in placeholders)
        {
            var placeholderValue = placeholder.Value;
            var placeholderPropertyName = placeholderValue.Replace("{", "").Replace("}", "");
            var property = interpolationContext.GetType().GetProperty(placeholderPropertyName);
            var value = property?.GetValue(interpolationContext)?.ToString() ?? "";
            self = self.Replace(placeholderValue, value);
        }
        return self;
    }
}

And use it like

    [Fact]
    public void Foo()
    {
        var world = "World";
        var someInt = 42;
        var unused = "Not used";

        //This is a normal string, it can be retrieved from config
        var myString = "Hello {world}, this is {someInt}";

        //You need to pass all local values that you may be using in your string interpolation. Pass them all as one single anonymous object.
        var result = myString.Interpolate(new {world, someInt, unused});

        result.Should().Be("Hello World, this is 42");
    }

EDIT: For dotted notation support: Credits go to this answer.

public static class StringExtensions
{
    public static string Interpolate(this string self, object interpolationContext)
    {
        var placeholders = Regex.Matches(self, @"\{(.*?)\}");
        foreach (Match placeholder in placeholders)
        {
            var placeholderValue = placeholder.Value;
            var placeholderPropertyName = placeholderValue.Replace("{", "").Replace("}", "");
            var value = GetPropertyValue(interpolationContext, placeholderPropertyName)?.ToString() ?? "";
            self = self.Replace(placeholderValue, value);
        }
        return self;
    }

    public static object GetPropertyValue(object src, string propName)
    {
        if (src == null) throw new ArgumentException("Value cannot be null.", nameof(src));
        if (propName == null) throw new ArgumentException("Value cannot be null.", nameof(propName));

        if (propName.Contains("."))
        {
            var temp = propName.Split(new char[] {'.'}, 2);
            return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
        }
        var prop = src.GetType().GetProperty(propName);
        return prop != null ? prop.GetValue(src, null) : null;

    }
}

To me, it's simpler to convert the interpolated string into a format string and then use the normal string.format.

private static string ConvertInterpolatedStringToFormartString(string interpolatedString)
{
   var placeholders = Regex.Matches(interpolatedString, @"\{(.*?)\}").ToArray();
   for (int i = 0; i < placeholders.Length; i++)
   {                    
       interpolatedString = interpolatedString.Replace(placeholders[i].ToString(), $"{{{i}}}");
   }
        return interpolatedString;
}
Related