As you might have seen, there has been some blogging about named format strings in the last days.
- Fun With Named Formats, String Parsing, and Edge Cases (Phil Haack)
- Named Formats Redux (Phil Haack)
- (James.ToString)
If you didn't came across it, the general idea is to use something like:
"foo '{foo}' bar: {bar}".Format(new {foo = "abc", bar= 123})
Since I already had an analog solution for this problem I thought it would be fun to run it in 'competition' with the other implementations. After I adopted it the the same syntax (I used \{ and \} as escape sequences), a bit of optimisation and make it pass the Phil's test suite, it turned out to run quite fast.
These are the results (output slightly changed from Phil's implementation), including an ordinary string.Format with a object array in comparison:
Hanselformat took 0.21862 ms / 00:00:10.9318279 OskarFormat took 0.30212 ms / 00:00:15.1064949 JamesFormat took 0.17788 ms / 00:00:08.8948007 HenriFormat took 0.14302 ms / 00:00:07.1513749 HaackFormat took 0.1572 ms / 00:00:07.8600728 HartFormat took 0.13706 ms / 00:00:06.8530890
Mine: RobotoFormat took 0.12632 ms / 00:00:06.3163623
Standard string.Format: str.Format took 0.00786 ms / 00:00:00.3935486
Find my code below. If you find any bugs, please let me know!
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text;
namespace StringLib { public static class RobotoFormatter { //------------------------------------------------------------------ ///
Descriptor[] descriptors = GetDescriptors(format);
string result = format; Descriptor descriptor; object descriptorValue;
//could be done with foreach, altough 'for' could be faster. for (int i = 0; i < descriptors.Length; i++) { descriptor = descriptors[i]; descriptorValue = GetDescriptorValue(@object, descriptor); result = result.Replace( string.Concat("{", descriptor.FullDescriptor,"}"), descriptor.ValueToString(descriptorValue)); } return result.Replace(@"{{", "{").Replace(@"}}", "}"); }
//--------------------------------------------------------- private static object GetDescriptorValue(object @object, Descriptor descriptor) { object result = @object; foreach (var name in descriptor.Names) { PropertyInfo descriptionInfo = result.GetType.GetProperty(name); if (descriptionInfo == null) { throw new FormatException(string.Format("No property named '{0}' found. Invalid format!", descriptor)); } result = descriptionInfo.GetValue(result, null); } return result; }
//------------------------------------------------------------------ ///
bool isDescriptor = false; char character; for (int i = 0; i < format.Length; i++ ) { character = format[i]; //ignore escaped opening/closing braces if (( character == '{' && GetNextChar(i, format) == '{' ) || ( character == '}' && GetNextChar(i, format) == '}')) { i++; continue; }
if (isDescriptor || character == '{') //we found beginning { isDescriptor = true; if (character!= '}') { if (character!= '{') { descriptorBuilder.Append(character); } } else if (GetNextChar(i, format)!= '}') //found an end (ignoring escaped closing) { var descriptor = new Descriptor(descriptorBuilder.ToString); if (!list.Contains(descriptor)) { list.Add(descriptor); }
descriptorBuilder.Remove(0, descriptorBuilder.Length); isDescriptor = false; } } } if (isDescriptor) //found no end { throw new FormatException("No end for descriptor found. Please use {{ to encode a { in your format."); } return list.ToArray; }
//--------------------------------------------------------- private static char GetNextChar(int i, string format) { if (((i + 1) < format.Length)) { return format[i + 1]; } return ' '; }
//--------------------------------------------------------- ///
Format = string.Empty; FullDescriptor = descriptorValue;
string[] values = descriptorValue.Split(':'); if (values.Length > 0) { Names = values[0].Split('.'); } if (values.Length > 1) { Format = values[1]; } }
//--------------------------------------------------------- public string[] Names { get; private set; } public string Format { get; private set; } public string FullDescriptor { get; private set; }
//--------------------------------------------------------- public bool HasFormat { get { return!string.IsNullOrEmpty(Format); } }
//--------------------------------------------------------- public string ValueToString(object value) { return string.Format(ToFormatString, value); }
//--------------------------------------------------------- private string ToFormatString { string formatString = "{0}"; if (HasFormat) { formatString = string.Concat("{0:", Format, "}"); } return formatString; }
//--------------------------------------------------------- public bool Equals(Descriptor other) { return FullDescriptor.Equals(other.FullDescriptor); } } } }
Nothing Impossible roboto
