Type custom format string

Type custom format string

introduction

String is probably the most used type, ToString () should be all the most used method. However, it should not just be used to output the name of the type, if used properly, it can be easily output format our custom type. This article will be discussed step by step ToString (), and related IFormattable, IFormatProvider ICustomFormatter and interfaces.

The ability to provide custom format string in internal type

ToString System.Object inherited from the base class ()

String is one that people can directly read data types, in many cases we would expect to get a string type of output. Therefore, Microsoft .Net Framework provides the base class for all types of System.Object in a virtual ToString () method, its default implementation is to return the object type name.

Suppose we have such a type that defines some information "friends" of the object:

namespace CustomToString
   public class Friend {
       private string familyName;    // 姓
       private string firstName;     // 名

       public Friend(string familyName, string firstName){
          this.familyName = familyName;
          this.firstName = firstName;
       }   
       public Friend(): this("张","子阳"){}

       public string FamilyName {
          get { return familyName; }
       }

       public string FirstName {
          get { return firstName; }
       }
   }
}

When we call ToString () method on a Friend instance, it will return the name of the type: CustomToString.Friend.

Friend f = new Friend();
Console.WriteLine(f.ToString()); // 输出:CustomToString.Friend

Covering ToString () method

In the example above, regardless of the type of instances (objects) included in the data (field values), it will always return the same result (CustomToString.Friend). In many cases, returning an object of type name does not mean much for us to get on top of it, we may be more desirable to be able to return a friend's name (value famliyName and firstName fields). At this time, we can simply overwrite the base class ToString System.Object () method, a method of adding the Friend class:

// 覆盖System.Object基类的 ToString() 方法
public override string ToString() {
   return String.Format("Friend: {0}{1}", familyName, firstName);
}

At this point, we once again run the code:

F = new new friend Friend ();
Console.WriteLine (f.ToString ()); // Output: Friend: Zhang Zaiyang
f = new Friend ( "King", "Tao");
Console.WriteLine (f.ToString ()) ; // output: Friend: Wang Tao

You can see different objects, ToString () object based on field values ​​returned different results, so for us it will be more meaningful.

Overload ToString () method

Sometimes, we may need to be formatted objects in different ways. Take the Friend type is: Westerners name first, followed by the surname; and Chinese people is the former name, the name in the post. So if you run the following code, although the program can not go wrong, but from a linguistic point of view there is a problem in English:

Friend a = new Friend("Zhang", "Jimmy");
Console.WriteLine(a.ToString());    // 输出:Friend: ZhangJimmy

And we expect the output is: Jimmy Zhang. This time, you may want to think about .Net Framework solution to this problem adopted: overloaded ToString (). Let ToString () method takes one parameter, be formatted in accordance with this parameter. Such int a = 123; Console.WriteLine (a.ToString ( "c")); specifies the character string "c" as a parameter, generating monetary types of output: ¥ 123.00. We can also use this method to improve the Friend class, overloading a ToString () method in the Friend, so as to define a character string formatted in accordance with the parameters:

// string parameter is defined according to the type of format
public String the ToString (String the format) {
    Switch (format.ToUpper ()) {
       Case "W is": @ West: Western
           return String.Format ( "Friend: {0 } . 1} { ", firstName, familyName);
       Case" E ": // East: Oriental
           return this.toString ();
       Case" G ": // General
       default:
           return base.ToString ();
    }
}

Then we can use the overloaded version when using the ToString () method, the English name, we pass "W" as a parameter, this would resolve the above problem:

Friend f = new Friend();
Console.WriteLine(f.ToString());        // 输出:Friend: 张子阳

f = new Friend("Zhang", "Jimmy");
Console.WriteLine(f.ToString("W")); // 输出:Friend: Jimmy Zhang

NOTE: This problem better solution is not overloaded ToString (), you can simply use the property to complete, like this:

public string WesternFullName{
    get{ return String.Format("{0} {1}", firstName, familyName)}
}
public string EasternFullName{
    get{ return String.Format("{0}{1}", familyName, firstName)}
}

In this article, I am here to just give an example to illustrate, never mind so I have to use the property in this way, and the back is the same.

Achieve IFormattable Interface

We provide users with the Friend category, although overloaded ToString () can cope with cultural differences East / West, but the user's needs are always changing: we stand to think about the type designer's perspective. For example, the user is a Web developer, and it is desirable names are always displayed in bold in order to avoid all out and then each operation attribute format, as long as he will want to apply on the type ToString () on it to achieve the desired effect, it will be more easy some, such as:

F = new new friend Friend ();
Label1.Text the String.Format = ( "<B> {{0}}. 1 </ B>", f.familyName, f.firstName);
// this is more convenient (user desires ):
// Label1.Text = f.ToString (***);

At this point we provide formatting method there is no way true. For unforeseen circumstances, we want to allow users to decide how to format string object. Microsoft apparently thought of this problem and provides an interface IFormattable for us. When you, as a type designer, desirable to provide a custom format for your users ToString (), you can implement this interface. We now look at this interface definition:

public interface IFormattable {
    string ToString(string format, IFormatProvider formatProvider);
}

It contains only a method ToString (): Parameters format with our previous section overloaded ToString same meaning as format () method for how to format parameter values ​​is determined; IFormatProvider parameter formatProvider is a type, which is defined as as follows:

public interface IFormatProvider {
    object GetFormat(Type formatType);
}

Wherein formatType is the current instance of the object type (Another possibility is ICustomFormatter, there is described later) --type object. In this example, we Friend this type is formatted, then this value is equivalent formatType typeof (Friend), or f.GetType () (f Friend type is an example). The GetFormat () method returns an object of type Object, the actual operation performed by the format object that implements the interface ICustomFormatter, it contains only one method, Format ():

public interface ICustomFormatter{
   string Format(string format, object arg, IFormatProvider formatProvider);
}

Wherein the meaning of format the same as above, arg Examples of the type of format is to be here is one example of Friend, formatProvider generally not used herein.

See here you may feel a bit confusing, in fact, as long as you remember: As a type designer, you only need to implement IFormattable interfaces on it: first by IFormatProvider.GetFormat parameters provider's () method to obtain a ICustomFormatter object again further objects of the call ICustomFormatter Format () method then returns Format () method returns the value:

public class Friend: IFormattable{
   // 略 ...

    // 实现 IFormattable 接口
    public string ToString(string format, IFormatProvider provider) {
       if (provider != null) {
           ICustomFormatter formatter =
              provider.GetFormat(this.GetType()) as ICustomFormatter;
           if (formatter != null)
              return formatter.Format(format, this, provider);
       }

       return this.ToString(format);
    }
}

Place above caveat is IFormatProvider.GetFormat () method of the type of information currently Friend object (by this.GetType ()) passes inside.

Designers of the type of work here on the end, and now let us look to achieve this type of IFormattable, types of users how to use their own definition of the object string format. As the types of users, in order to enable custom format string object, and the need to implement IFormatProvider ICustomFormatter interfaces. At this point there are two strategies:

  1. The establishment of a class, such as call FriendFormatter, this class implements IFormatProvider and ICustomFormatter interface.
  2. Establish two classes, such as call ObjectFormatProvider and FriendFormatter, respectively, to achieve ICustomFormatter and IFormatProvider interfaces, and let's GetFormat ObjectFormatProvider () method returns an instance of a FriendFormatter.

Let's take a look at the first strategy:

public class FriendFormatter : IFormatProvider, ICustomFormatter {
    // 实现 IFormatProvider 接口,由 Friend类的 IFormattable.ToString()方法调用
    public object GetFormat(Type formatType) {
       if (formatType == typeof(Friend))
           return this;
       else
           return null;
    }

    // 实现 ICustomFormatter 接口
    public string Format(string format, object arg, IFormatProvider formatProvider) {
       //if (arg is IFormattable)
       //  return ((IFormattable)arg).ToString(format, formatProvider);

       Friend friend = arg as Friend;
       if (friend == null)
           return arg.ToString();

       switch (format.ToUpper()) {
           case "I":
             return String.Format("Friend: <i>{0}{1}<i>" ,friend.FamilyName, friend.FirstName);
           case "B":
             return String.Format("Friend: <b>{0}{1}<b>", friend.FamilyName, friend.FirstName);
           default:
              return arg.ToString();
       }
    }
}

The above binding ToString () method with the view, the process here is very clear: When used in this manner, the GetFormat the sentence is determined, if (formatType == typeof (Friend )) can be applied to ensure FriendFormatter class type of the object Friend format. Subsequently, referred to by this keyword returns the current FriendFormatter object. Because FriendFormatter ICustomFormatter also implements the interface, so the type IFormattable.ToString Friend () method can be converted to a FriendFormater ICustomFormatter type, and then call the ICustomFormatter.Format () method to return the desired results.

NOTE: Note commented upper part, may be a reference MSDN because of it, some people in the realization ICustomFormatt, and will add that part of the statement. Indeed MSND example using a Long, and using the String.Format () overloads to customize format, herein vary. When you block out the above comments, it is clear that the formation of an infinite loop.

We now look to test the above code:

Friend f = new Friend();
FriendFormatter formatter = new FriendFormatter();
Console.WriteLine(f.ToString("b", formatter));    // 输出:Friend: <b>张子阳</b>

Next we look at the second way, the IFormatProvider ICustomFormatter and handed over to different classes to implement:

ObjectFormatProvider class public: IFormatProvider {
    // implement IFormatProvider interface called by ToString () method of class Friend
    public Object the GetFormat (the Type formatType) {
       IF (formatType == typeof (Friend))
           return new new FriendFormatter (); // return a realization examples of the types of ICustomFormatter
       the else
           return null;
    }
}

// ICustomFormatter implement interfaces, providing a specific format for the service is always type (eg Friend)
public class FriendFormatter: ICustomFormatter {

    // implement interfaces ICustomFormatter
    public string format (string format, object Arg, the IFormatProvider formatProvider) {
       // IF (the IFormattable Arg IS)
       // return ((the IFormattable) Arg) .ToString (the format, formatProvider);

       Friend friend = arg as Friend;
       if (friend == null)
           return arg.ToString();

       switch (format.ToUpper()) {
           case "I":
             return String.Format("Friend: <i>{0}{1}<i>", friend.FamilyName, friend.FirstName);
           case "B":
             return String.Format("Friend: <b>{0}{1}<b>", friend.FamilyName, friend.FirstName);
           default:
              return arg.ToString();
       }
    }
}

The above method looks almost the same, but the difference is a class split into two. Indeed, split into two classes will be more flexible: the use of a class implements two interfaces manner when, FriendFormatter Friend only to format type. If we have a Book class, similarly, we need to create a BookFormatter.

And it will be split into two classes, just need to create a class that implements the interface ICustomFormatter again, and then ObjectFormatProvider do a little modification on it. At this point Provider class can be seen as a generic class that can provide services for multiple types of format. Book Now suppose we have a type, so we only need to modify ObjectFormatProvider class on it:

ObjectFormatProvider class public: IFormatProvider {
    // implement IFormatProvider interface called by ToString () method of class Friend
    public Object the GetFormat (the Type formatType) {
       IF (formatType == typeof (Friend))
           return new new FriendFormatter ();
       IF (== formatType typeof (Book))
           return new new BookFormatter (); // returns a target BookFormatter
       the else
           null return;
    }
}
// ... omitted type BookFormatter

The ability to provide custom format strings in the external type

Now we stand on a type of user perspective to think about: In many cases, the designers did not realize the type of IFormattable interface type, then how do we deal with it? Let us think about the treatment of .Net Framework:

int a = 123;
Console.WriteLine(a.ToString("c"));        // 输出: ¥123.00
Console.WriteLine(String.Format("{0:c}", a));  // 输出: ¥123.00

In fact, String.Format () also provides an overloaded method, a reception IFormatProvider object that is defined by our own IFormatProvider, for the formatting effect we need. According to the above comparison, we do a summary: In order to achieve the type of custom format strings, we always need to implement the IFormatProvider interface. If the type implements IFormattable interface, we can call on type ToString () method, passing IFormatProvider objects; if IFormattable type does not implement the interface, we can () static method, passing IFormatProvider object through the String.Format.

Now we have to create the type of implement IFormatProvider interface, and way above some slightly different: by Reflector tool (do not know can go to Baidu) can be seen, call the String.Format () creates an internal StringBuilder type when Object builder, then call builder.AppendFormat (provider, format, args); inside this method will eventually call the provider of GetFormat () method:

formatter = (ICustomFormatter) provider.GetFormat(typeof(ICustomFormatter));

You can see, provider.GetFormat () passed a typeof (ICustomFormatter) objects. Thus, if the judgment is not to be used by the String.Format IFormatProvider () in this way, external type, is not only determined formatType equal typeof (ICustomFormatter) can be:

OutFriendFormatter class public: IFormatProvider, the ICustomFormatter
{
    // IFormatProvider interfaces implemented by the class ToString Friend () method call
    public Object the GetFormat (the Type formatType)
    {
       IF (formatType == typeof (the ICustomFormatter))  
           return the this;
       the else
           return null;
    }
   
    / / ICustomFormatter achieve slightly   
}

Once again, we look at the code test:

Friend f = new Friend();
OutFriendFormatter formatter = new OutFriendFormatter();
string output = String.Format(formatter, "{0:i}", f);
Console.WriteLine(output);      // Friend: <i>张子阳<i>

An example of the .Net implemented IFormatProvider

The most common used .Net IFormatProvider An example is the CultureInfo class. In many cases, the amount we need to be formatted, then we usually like this:

int money = 100;
Console.WriteLine(String.Format("{0:c}", money));

We expect the results of this output is ¥ 100.00. However, this is not always the case, when you run this program on Chinese operating system, it will certainly get ¥ 100.00 as you wish; but in the English operating system, you probably will get a $ 100.00. This is because in the digital display in the amount fashion, will make judgments based on the locale of the current system, if you do not explicitly specified locale, it will by default locale to the appropriate display. In .Net, the locale of the class is to encapsulate CultureInfo, and it implements IFormatProvider, when we need to clearly specify how the amount, can make use of this class to complete:

int money = 100;
IFormatProvider provider = new CultureInfo("zh-cn");
Console.WriteLine(String.Format(provider, "{0:c}", money));    // 输出:¥100.00

provider = new CultureInfo("en-us");
Console.WriteLine(String.Format(provider, "{0:c}", money));    // 输出:$100.00

to sum up

In this article, I systematically discusses how to customize the type of format. We have a variety of ways to achieve this purpose: covering ToString (), overloaded ToString (), implement IFormatProvider interface. We also discussed the two ways IFormatProvider and ICustomFormatter: Create a class that implements them, or each implement a different category.

I think many people will use these methods in reading this before, I'm here to hope that we can carry out more than Reflections, to a designer's point of .Net framework to think about: Why design a three interfaces with String.Format () static class to implement this process? What this design provides flexibility? From this article, I expect you to harvest more than how to use these types as a framework for the user, but as a designer to design a framework for this type of structure.

Thanks for reading, I hope this article can give you help!

Reproduced in: https: //www.cnblogs.com/JimmyZhang/archive/2008/05/30/1210378.html

Guess you like

Origin blog.csdn.net/weixin_34072159/article/details/93444037