понедельник, 18 июня 2012 г.

C#: Labels instead of numbers in String.Format

Can be labels used instead of numbers in String.Format?

No, they aren't by default. But it's possible by the very nice Scott Hanselman's extention method for it:

public static class FormattableObject
{
    public static string ToString(this object anObject, string aFormat)
    {
        return FormattableObject.ToString(anObject, aFormat, null);
    }
 
    public static string ToString(this object anObject, string aFormat, IFormatProvider formatProvider)
  {
       StringBuilder sb = new StringBuilder();
     Type type = anObject.GetType();
     Regex reg = new Regex(@"({)([^}]+)(})",RegexOptions.IgnoreCase);
        MatchCollection mc = reg.Matches(aFormat);
      int startIndex = 0;
     foreach(Match m in mc)
      {
           Group g = m.Groups[2]; //it's second in the match between { and }
           int length = g.Index - startIndex -1;
           sb.Append(aFormat.Substring(startIndex,length));
             
            string toGet = String.Empty;
            string toFormat = String.Empty;
         int formatIndex = g.Value.IndexOf(":"); //formatting would be to the right of a :
           if (formatIndex == -1) //no formatting, no worries
          {
               toGet = g.Value;
            }
           else //pickup the formatting
            {
               toGet = g.Value.Substring(0,formatIndex);
               toFormat = g.Value.Substring(formatIndex+1);
            }
 
           //first try properties
          PropertyInfo retrievedProperty = type.GetProperty(toGet);
           Type retrievedType = null;
          object retrievedObject = null;
          if(retrievedProperty != null)
           {
               retrievedType = retrievedProperty.PropertyType;
             retrievedObject  = retrievedProperty.GetValue(anObject,null);
           }
           else //try fields
           {
               FieldInfo retrievedField = type.GetField(toGet);
                if (retrievedField != null)
             {
                   retrievedType = retrievedField.FieldType;
                   retrievedObject = retrievedField.GetValue(anObject);
                }
           }
 
           if (retrievedType != null ) //Cool, we found something
          {
               string result = String.Empty;
               if(toFormat == String.Empty) //no format info
               {
                   result = retrievedType.InvokeMember("ToString",
                     BindingFlags.Public | BindingFlags.NonPublic |
                     BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase
                     ,null,retrievedObject,null) as string;
              }
               else //format info
              {
                   result = retrievedType.InvokeMember("ToString",
                     BindingFlags.Public | BindingFlags.NonPublic |
                     BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase
                     ,null,retrievedObject,new object[]{toFormat,formatProvider}) as string;
             }
               sb.Append(result);
          }
           else //didn't find a property with that name, so be gracious and put it back
            {
               sb.Append("{");
             sb.Append(g.Value);
             sb.Append("}");
         }
           startIndex = g.Index + g.Length +1 ;
        }
       if (startIndex < aFormat.Length) //include the rest (end) of the string
      {
           sb.Append(aFormat.Substring(startIndex));
       }
       return sb.ToString();
   }
}

It's faster then others. Use it with objects:
Person p = new Person();
string foo = p.ToString("{Money:C} {LastName}, {ScottName} {BirthDate}");

Комментариев нет:

Отправить комментарий