String Format with Named Arguments


/ Published in: C#
Save to your folder(s)



Copy this code and paste it in your HTML
  1. class Program
  2. {
  3. static void Main()
  4. {
  5. var someObject = new { FirstName = "John", LastName = "McDowall", Dob = new DateTime(1976, 1, 14) };
  6.  
  7. var format = new StringFormatCompiler().Compile("{FirstName} {LastName}, born {Dob:D}");
  8. var output = new Formatter().Process(format, someObject);
  9.  
  10. Console.WriteLine(output);
  11. }
  12. }
  13.  
  14. public class Formatter
  15. {
  16. public string Process(StringFormatWithNamedArguments formatWithNamedArguments, object valueProvider)
  17. {
  18. var valueList = new object[formatWithNamedArguments.ArgumentNames.Count];
  19. for (var index = 0; index < valueList.Length; index++)
  20. {
  21. var argumentName = formatWithNamedArguments.ArgumentNames[index];
  22. var value = GetValue(valueProvider, argumentName);
  23. valueList[index] = value;
  24. }
  25. return string.Format(formatWithNamedArguments.Format, valueList);
  26. }
  27.  
  28. private static object GetValue(object valueProvider, string name)
  29. {
  30. var type = valueProvider.GetType();
  31. var propertyInfoOrDefault = type.GetProperties().Where(x => x.Name == name).SingleOrDefault();
  32. if (propertyInfoOrDefault != null)
  33. {
  34. var methodInfo = propertyInfoOrDefault.GetGetMethod(true);
  35. if (methodInfo != null)
  36. {
  37. return methodInfo.Invoke(valueProvider, new object[] {});
  38.  
  39. }
  40. }
  41. var field = type.GetFields().Where(x => x.Name == name).SingleOrDefault();
  42. if (field != null)
  43. {
  44. return field.GetValue(valueProvider);
  45. }
  46. throw new ArgumentException("Could not find get property or field for " + name + " on type " + type);
  47. }
  48. }
  49.  
  50. public class StringFormatWithNamedArguments
  51. {
  52. public string Format { get; private set; }
  53. public List<string> ArgumentNames { get; private set; }
  54.  
  55. public StringFormatWithNamedArguments(string format, List<string> argumentNames)
  56. {
  57. Format = format;
  58. ArgumentNames = argumentNames;
  59. }
  60. }
  61.  
  62. public class StringFormatCompiler
  63. {
  64. public StringFormatWithNamedArguments Compile(string format)
  65. {
  66. var tokens = Tokenize(format);
  67. var stringBuilder = new StringBuilder();
  68.  
  69. var argumentNames = new List<string>();
  70.  
  71. var argumentIndex = 0;
  72. foreach (var token in tokens)
  73. {
  74. if (token is Literal)
  75. {
  76. stringBuilder.Append(token);
  77. }
  78. else
  79. {
  80. var argument = (Argument) token;
  81. stringBuilder.Append('{').Append(argumentIndex).Append(argument.GetSpecification()).Append('}');
  82. argumentIndex++;
  83. argumentNames.Add(argument.GetName());
  84. }
  85. }
  86. return new StringFormatWithNamedArguments(stringBuilder.ToString(), argumentNames);
  87. }
  88.  
  89. public IEnumerable<Token> Tokenize(string format)
  90. {
  91. var result = new List<Token> {new Literal()};
  92.  
  93. foreach (var c in format.ToCharArray())
  94. {
  95. if (result.Last() is Literal)
  96. if (c == '{')
  97. result.Add(new Argument());
  98. else
  99. result.Last().Add(c);
  100. else
  101. if (c == '}')
  102. result.Add(new Literal());
  103. else
  104. result.Last().Add(c);
  105. }
  106.  
  107. return result.Where(x => x.HasData());
  108. }
  109. }
  110.  
  111. public abstract class Token
  112. {
  113. protected readonly StringBuilder Data = new StringBuilder();
  114.  
  115. public void Add(char c)
  116. {
  117. Data.Append(c);
  118. }
  119.  
  120. public bool HasData()
  121. {
  122. return Data.Length > 0;
  123. }
  124.  
  125. public override string ToString()
  126. {
  127. return Data.ToString();
  128. }
  129. }
  130.  
  131. public class Literal : Token
  132. {
  133.  
  134. }
  135.  
  136. public class Argument : Token
  137. {
  138. public bool HasSpecification()
  139. {
  140. return IndexOfSpecification() != -1;
  141. }
  142.  
  143. public string GetSpecification()
  144. {
  145. var indexOfSpecification = IndexOfSpecification();
  146. return indexOfSpecification == -1 ? null : Data.ToString().Substring(indexOfSpecification);
  147. }
  148.  
  149. private int IndexOfSpecification()
  150. {
  151. for (var index = 0; index < Data.Length; index++)
  152. {
  153. var c = Data[index];
  154. if (c == ':')
  155. {
  156. return index;
  157. }
  158. }
  159. return -1;
  160. }
  161.  
  162. public string GetName()
  163. {
  164. var indexOfSpecification = IndexOfSpecification();
  165. return indexOfSpecification == -1 ? Data.ToString() : Data.ToString().Substring(0, indexOfSpecification);
  166. }
  167. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.