In the configuration JSON file, the following lines are used:

{ "logFilePattern" : "{yyyy}.{mm}.{dd}.log" } 

Many different textual placeholders are used for the replacement: calculated file system paths that depend on the user, etc .. They are supposed to be replaced and for this, in previous versions of C #, composite formatting was used (I could be wrong in the term) in this way:

 string name = "Fred"; Console.WriteLine(String.Format("Name = {0}", name)); // output: "Name = Fred" 

With the release of the C # 6 version, it became possible to use named arguments (I could be wrong in the term) as follows:

 string name = "Fred"; Console.WriteLine($"Name = {name}"); // output: "Name = Fred" 

Question: how to interpolate a string with named arguments stored in a variable?

 string stringPattern = "Name = {name}"; string name = "Fred"; // ? 


I found here that it is not possible, because code:

 string name = "Fred"; string name2 = $"Name = {name}"; 

, the compiler is given to:

 string name = "fred"; string name2 = String.Format("Name = {0}", name); 

In other words, it's just syntactic sugar.

2 answers 2

UPD

If the whole question is how to generate a log file name, can it be easier to do this way? In the configuration, we set the valid with tz. .NET format for date:

 { "logFilePattern" : "yyyy.MM.dd.log" } 

Then in the code we bite it and get the file name:

 const string extension = ".log"; var datePattern = logFilePattern.Remove(logFilePattern.IndexOf(extension), extension.Length); DateTime date = DateTime.UtcNow; var logFileName = date.ToString(datePattern) + extension; 

About the task in general

A naive implementation can simply consist in replacing a value:

 string stringPattern = "Name = {name}"; string name = "Fred"; string formatted = stringPattern.Replace($"{{nameof(name)}}", name); 

There are two drawbacks:

  • non-universal code (I would like to have a separate method, such as FormatString(pattern, value1, value2, ...) )
  • the possible escaping of curly brackets is not taken into account (the string "{{name}}" not a pattern, but the replacement will be made)
  • in the presence of several values ​​we will produce extra lines

A method of the form FormatString(pattern, value1, value2, ...) , as far as I understand, is unrealistic, since there is no way in the called method to find out the names of the parameters with which it was called. Therefore, you can dwell on, for example, the dictionary:

 public static string FormatString(string pattern, IDictionary<string, object> values) 

Escaping can be processed with a regular expression, but given that there can be several parameters, this can also be an unnecessarily heavy operation. Therefore, the implementation in general depends on the desired degree of optimization, ideally, it is necessary to manually pass the char[] / List<char> and change it on the fly.

At night looking like this:

 public static string FormatString(string pattern, IDictionary<string, object> values) { var buffer = pattern.ToList(); int replaceStartIndex = -1; int replaceEndIndex = -1; int index = 0; while (index < buffer.Count) { var character = buffer[index]; if (character == '{') { if (replaceStartIndex == -1) { replaceStartIndex = index; } else { replaceStartIndex = -1; } } else if (character == '}') { if (replaceEndIndex == -1) { replaceEndIndex = index; } else { replaceEndIndex = -1; } } if (replaceStartIndex > -1 && replaceEndIndex > -1) { var key = new string(buffer.Skip(replaceStartIndex + 1).Take(replaceEndIndex - replaceStartIndex - 1).ToArray()); var value = values[key]; var stringValue = value?.ToString(); buffer.RemoveRange(replaceStartIndex, replaceEndIndex - replaceStartIndex + 1); if (stringValue != null) { buffer.InsertRange(replaceStartIndex, stringValue.ToCharArray()); index = replaceStartIndex + stringValue.Length; } else { index = replaceStartIndex; } replaceStartIndex = -1; replaceEndIndex = -1; } else { index++; } } return string.Join("", buffer); } 

Check:

 static void Main(string[] args) { string stringPattern = "Name = {name}, Age = {age}, Null = {nullString}, Escape = {{escape}}"; string name = "Fred"; int age = 42; string nullString = null; string escape = "You shouldn't see this"; var values = new Dictionary<string, object>() { [nameof(name)] = name, [nameof(age)] = age, [nameof(nullString)] = nullString, [nameof(escape)] = escape }; string formatted = FormatString(stringPattern, values); Console.WriteLine(formatted); } 

Conclusion:

Name = Fred, Age = 42, Null =, Escape = {{escape}}

  • There are quite a few examples on the network (for example: james.newtonking.com/archive/2008/03/29/… ), but you need to get exactly the same behavior as the original. - XelaNimed
  • @XelaNimed and what is not happy with the example of the link? The result is the same as that. What do you mean by "the same behavior"? - andreycha
  • In the given example it is necessary to transfer the object containing the data for replacement. When using the same standard method, the lines are interpolated by variables from the current scope. - XelaNimed
  • @XelaNimed Well, in the example you cited, it is easy to replace source with a dictionary, you get a call with about the same signature as me and with variables from the current scope. Add something to the question of pseudocode, as you imagine. - andreycha
  • I c # just started learning, but I think it should be possible to implement it without crutches. The @ sign can also be used with a variable, most likely in some way here too. Otherwise, of course you will have to drive through the dictionary. - XelaNimed

It says here that the string given in the topic is a System.FormattableString type. Regarding which we read what can be obtained by the index the desired injected parameter. And we can just form a new line with our inject. This is possible within the framework of the transmitted interpolated string to a function, for example .. Is it possible to get the desired result this way ?!

  • I'm not sure that I understood correctly, but it seems to me that you did not catch the essence of my question. First, the given string is not System.FormattableString , but System.String . The problem is to cast the regular string in the variable to System.FormattableString . - XelaNimed
  • Can you add sample code to make it clear how FormattableString can help in this case? - Grundy
  • @Grundy in FormattableString could use the docs.microsoft.com/en-us/dotnet/api/… method - XelaNimed
  • @XelaNimed, I see :) - Grundy