IFormattable和IFormatProvider

IFormattable和IFormatProvider用于格式化输出,比如日期格式(2020/3/18 19:56:29、2020年3月18日)、数字格式(小数位、数值类型decimal/float/double)、其他需要规范输出的应用场景。
IFormattable 接口将对象的值格式化为字符串表示形式。
IFormatProvider 接口检索对象以控制格式化。

IFormattable 接口的示例为例,根据摄氏度,格式化输出对应华氏度和开尔文。
格式化类型包括G(默认,摄氏度)、C(摄氏度)、F(华氏度)、K(开尔文)。

单独使用IFormattable

IFormattable 的ToString方法接受一个代表格式的字符串参数,通过对这个参数的分析来进行格式化输出。
需要格式化的类直接实现IFormattable 接口,Temperature(温度)类直接实现IFormattable接口。

public class Temperature : IFormattable
{
	private decimal temp;

	public Temperature(decimal temperature)
	{
		if (temperature < -273.15m)
			throw new ArgumentOutOfRangeException(String.Format("{0}低于绝对零度",temperature));
		this.temp = temperature;
	}

	///
	/// 摄氏度
	///
	public decimal Celsius
	{
		get { return temp; }
	}

	///
	/// 华氏温度
	///
	public decimal Fahrenheit
	{
		get { return temp * 9 / 5 + 32; }
	}

	///
	/// 开尔文(热力学温标或称绝对温标)
	///
	public decimal Kelvin
	{
		get { return temp + 273.15m; }
	}

	public override string ToString()
	{
		return this.ToString("G", CultureInfo.CurrentCulture);
	}

	public string ToString(string format)
	{
		return this.ToString(format, CultureInfo.CurrentCulture);
	}

	public string ToString(string format, IFormatProvider provider)
	{
		if (String.IsNullOrEmpty(format)) format = "G";
		if (provider == null) provider = CultureInfo.CurrentCulture;
                
                //G(默认,摄氏度)、C(摄氏度)、F(华氏度)、K(开尔文)
		switch (format.ToUpperInvariant())
		{
			case "G":
			case "C":
				return temp.ToString("F2", provider) + " °C";
			case "F":
				return Fahrenheit.ToString("F2", provider) + " °F";
			case "K":
				return Kelvin.ToString("F2", provider) + " K";
			default:
				throw new FormatException(String.Format("{0}格式不被支持", format));
		}
	}
}
Temperature temp1 = new Temperature(0);
//带有格式化字符串C/K/F
Console.WriteLine("{0:C} (Celsius) = {0:K} (Kelvin) = {0:F} (Fahrenheit)\n", temp1);
//其他写法见示例代码

使用IFormatProvider

String.Format方法接受一个IFormatProvider类型的参数,以允许类型的使用者提供格式化的方法。

public class Temperature
{
	internal decimal temp;

	public Temperature(decimal temperature)
	{
		if (temperature < -273.15m)
			throw new ArgumentOutOfRangeException(String.Format("{0}低于绝对零度.",temperature));
		this.temp = temperature;
	}
}

单独写自定义FormatProvider类实现格式化输出,并在格式化方法中加入这个FormatProvider类对象。

public class TemperatureFormatProvider : IFormatProvider, ICustomFormatter
{
	public string Format(string format, object arg, IFormatProvider provider)
	{
		if (arg.GetType() != typeof(Temperature))
			throw new FormatException(String.Format("类型 '{0}' 是无效的.", arg.GetType().ToString()));

		decimal temp = (arg as Temperature).temp;

		if (String.IsNullOrEmpty(format)) format = "G";
		if (provider == null) provider = CultureInfo.CurrentCulture;

		switch (format.ToUpperInvariant())
		{
			case "G":
			case "C":
				return temp.ToString("F2", provider) + " °C";
			case "F":
				var Fahrenheit= temp * 9 / 5 + 32;
				return Fahrenheit.ToString("F2", provider) + " °F";
			case "K":
				var Kelvin= temp + 273.15m;
				return Kelvin.ToString("F2", provider) + " K";
			default:
				throw new FormatException(String.Format("{0}格式不被支持.", format));
		}
	}

	public object GetFormat(Type formatType)
	{
		if (formatType == typeof(ICustomFormatter))
			return this;
		else
			return null;
	}
}
Console.WriteLine(String.Format(new TemperatureFormatProvider(), "{0:C} (Celsius) = {0:K} (Kelvin) = {0:F} (Fahrenheit)\n", temp1));

示例代码

UseIFormattable
UseIFormatProvider

参考资料

IFormattable 接口
IFormatProvider 接口
.NET基础 (13)IFormattable和IformatProvider的使用

上一篇:MariaDB MySQL变量取值避免四舍五入的方法


下一篇:【洛谷6124】[NEERC2015] Binary vs Decimal(BFS)