andrewvos.com

projects - git - twitter - cv

object.Format Extension

2009-01-06

EDIT: I've added the string extension found

I was reading an article on haacked today, called Fun With Named Formats, String Parsing, and Edge Cases and I started thinking what if I made a format extension method for an object which used reflection to get the properties.

private static void test() {
  CultureInfo southAfrican = CultureInfo.CreateSpecificCulture("en-ZA");
  var human = new {
    FirstName = "Andrew",
    LastName = "Vos",
    BirthDate = new DateTime(1983, 10, 23),
    MoneyInPocket = 123.34
  };

  string formatted = human.Format(southAfrican,
    "Hello my name is {FirstName}, and my last name is {LastName}.\n" +
    "My birth date is {BirthDate:D}\n" +
    "I have  in my pocket right now.\n" +
    "Here are some random chars... { { } }{ {} {}{} }{ {} " +
    "\nThis last value fails because there's no corresponding property: {TheLastValue}."
  );

  Console.WriteLine(formatted);
}

Would result in the following output:

:::
Hello my name is Andrew, and my last name is Vos.
My birth date is 23 October 1983
I have {R 123.34} in my pocket right now.
Here are some random chars... { { } }{ {} {}{} }{ {}
This last value fails because there's no corresponding property: {TheLastValue}.

Here's the code:

internal static class ObjectFormatter {
  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Format(this object obj, string format) {
    return ObjectFormatter.Format(obj, null, format);
  }

  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <param name="provider">An <see cref="T:System.IFormatProvider"></see> that supplies culture-specific formatting information.</param>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Format(this object obj, IFormatProvider provider, string format) {
    if (format == null) throw new ArgumentNullException("format");
    if (obj == null) throw new ArgumentNullException("obj");

    const string propertyPattern = @"\{(?<PropertyName>[a-z,0-9,_]+)\:?(?<PropertyFormat>[^}]+)?\}";
    MatchCollection matches = Regex.Matches(format, propertyPattern,
      RegexOptions.Compiled |
      RegexOptions.CultureInvariant |
      RegexOptions.IgnoreCase);

    foreach (Match match in matches) {
      string propertyName = match.Groups["PropertyName"].Value;
      string propertyFormat = match.Groups["PropertyFormat"].Value;
      PropertyInfo propertyInfo = obj.GetType().GetProperty(propertyName);

      if (propertyInfo != null) {
        object propertyValue = propertyInfo.GetValue(obj, null);
        string propertyString = null;

        if (propertyValue != null) {
          propertyString = string.Format(provider, "{0:" + propertyFormat + "}", propertyValue);
        }
        format = format.Replace(match.Value, propertyString);
      }
    }

    return format;
  }
}

internal static class StringObjectInjector {
  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Inject(this string format, object obj) {
    return obj.Format(format);
  }

  /// <summary>Formats the string by replacing format items with the objects properties.</summary>
  /// <param name="format">A <see cref="T:System.String"></see> containing the format items.</param>
  /// <param name="provider">An <see cref="T:System.IFormatProvider"></see> that supplies culture-specific formatting information.</param>
  /// <param name="obj">The <see cref="T:System.Object"></see> to format.</param>
  /// <returns>A copy of format where the format items have been replace by the corresponding properties.</returns>
  /// <exception cref="T:System.ArgumentNullException">format or obj is null.</exception>
  internal static string Inject(this string format, IFormatProvider provider, object obj) {
    return obj.Format(provider, format);
  }
}