C#定义类型转化 及 格式化字符串
operator 关键字
operator 关键字用来重载内置运算符,或提供类/结构声明中的用户定义转换。它可以定义不同类型之间采用何种转化方式和转化的结果。
operator用于定义类型转化时可采用2种方式,隐式转换(implicit)和显示转换(explicit)
public class OperatorTestDemo
{
public static void Test()
{
OperatorTest mc = 1;//通过隐式装换,生成myclass对象
Console.WriteLine(mc.Value);
OperatorTest mc2 = new OperatorTest(2);
Console.WriteLine((int)mc2);//显示转化,调用myclass至int的处理方法
Console.WriteLine(mc2);//隐式转化,调用myclass至string的处理方法
}
}
public class OperatorTest
{
private int value;//声明value私有字段
public int Value//声明只读属性
{
get { return value; }
}
public OperatorTest(int value)//构造函数
{
this.value = value;
}
public static implicit operator OperatorTest(int value)//隐式声明的int转OperatorTest类处理方法
{
return new OperatorTest(value);
}
public static explicit operator int(OperatorTest mc)//显示声明的OperatorTest转int类处理方法
{
return mc.value;
}
public static implicit operator string(OperatorTest mc)//隐式声明的OperatorTest转string类处理方法
{
return ("定义的OperatorTest类string类型转化结果");
}
}
在利用implicit的隐式声明时,如果同时存在多个由当前类转化为其他类型数据的隐式声明的时候,可能出现2者都可以调用,编译器不知道选择哪个而出现的错误。
TypeConverter
[TypeConverter(typeof(StringToHumanTypeConverter))]
public class Human
{
public string Name { get; set; }
public Human Child { get; set; }
}
public class StringToHumanTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
else
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
return true;
else
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
Human h = new Human();
h.Name = value as string;
return h;
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
Human h = (Human)value;
return $"Human.Name:{(h.Name)}";
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
public static void Test()
{
TypeConverter homanTypeConverter = TypeDescriptor.GetConverter(typeof(Human));
if (homanTypeConverter.CanConvertFrom(typeof(string)))
{
Human h = (Human)homanTypeConverter.ConvertFrom("ssd");
Console.WriteLine(h.Name);
}
if (homanTypeConverter.CanConvertTo(typeof(string)))
{
Human h = new Human() { Name= "张飞"};
Console.WriteLine(homanTypeConverter.ConvertTo(h, typeof(string)));
}
}
格式化字符串
string 中定义的两个静态重载方法string.Format。
var msg = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}."
, "张飞", DateTime.Now, DateTime.Now.DayOfWeek);
-
string.Format方法中的不定参数args是一个数组,而format参数中的形如{0},{1}中的序号则是数组中对应的索引,所以最大序号必须小于参数个数(因为数组不能越界)。
-
{}是微软定义好的标记,用于分割format字符串。如果需要在字符串中包含大括号的话就必须进行转义,这个转义也和我们平时使用的"/"转义表示法不同,需要使用两个大括号进行转义如 {{ 或者 }},类似于逐字前缀字符@修饰的字符串中双引号的表示。
var msg2 = string.Format("Hello {{}},I am {0}", "张飞"); var msg3 = @"张飞""";
-
string.Format方法内部通过StringBuilder实现字符串的拼接。
-
形如"{ N [, M ][: formatString ]}"的格式化表示中:
- N是从0开始的整数,表示要格式化的参数的个数
- M是一个可选的整数,表示格式化后的参数所占的宽度,如果M是负数,那么格式化后的值就是左对齐的,如果M是正数,那么格式化后的值是右对齐的
- formatString为是另外一个可选的参数,表示格式代码
数字的格式化
标准格式化标识符
数学格式化为字符串时,格式代码通常是象‘X0’这样的格式。X是格式化标识符,0是精度标识符。格式标识符号共有9种,它们代表了大多数常用的数字格式。
字母 | 含义 |
---|---|
C或c | Currency 货币格式 |
D或d | Decimal 十进制格式(十进制整数,不要和.Net的Decimal数据类型混淆了) |
E或e | Exponent 指数格式 |
F或f | Fixed point 固定精度格式 |
G或g | General 常用格式 |
N或n | 用逗号分割千位的数字,比如1234将会被变成1,234 |
P或p | Percentage 百分符号格式 |
R或r | Round-trip 圆整(只用于浮点数)保证一个数字被转化成字符串以后可以再被转回成同样的数字 |
X或x | Hex 16进制格式 |
图形化格式字符串
如果标准格式化标识符还不能满足需求。可以使用图形化格式字符串来创建定制的字符串输出。
图形化格式化使用占位符来表示最小位数,最大位数,定位符号,负号的外观以及其它数字符号的外观。
符号 | 名称 | 含义 |
---|---|---|
0 | 0占位符 | 用0填充不足的位数 |
| 数字占位符 | 用#代替实际的位数
. | 十进制小数点 |
, | 千位分隔符 | 用逗号进行千位分割,比如把1000分割成1,000
% | 百分符号 | 显示一个百分标识
E+0,E-0,e+0,e-0 | 指数符号 | 用指数符号格式化输出
\ | 专一字符 | 用于传统格式的格式化序列,比如"\n"(新行)
'ABC',"ABC" | 常量字符串 | 显示单引号或者双引号里面的字符串
; | 区域分隔符 | 如果数字会被格式化成整数,负数,或者0,用;来进行分隔
,. | 缩放符号 | 数字除以1000
数字字符串的解析
string t = " -1,234,567.890 ";
double g1 = double.Parse(t);
Console.WriteLine("g1 = {0:F}", g1); //g1 = -1234567.89
//使用NumberStyles
double g2 = double.Parse(t,
NumberStyles.AllowLeadingSign |
NumberStyles.AllowDecimalPoint |
NumberStyles.AllowThousands |
NumberStyles.AllowLeadingWhite |
NumberStyles.AllowTrailingWhite);
Console.WriteLine("g2 = {0:F}", g2); //g2 = -1234567.89
//通过NumberFormatInfo的CurrencySymbol属性,兼容货币符号
string u = "¥ -1,234,567.890 ";
NumberFormatInfo ni = new NumberFormatInfo();
ni.CurrencySymbol = "¥";
double d = Double.Parse(u, NumberStyles.Any, ni);
Console.WriteLine("d = {0:F}", d); //h = -1234567.89
//通过CultureInfo,执行特定文化的操作
int k = 12345;
CultureInfo us = new CultureInfo("en-US");
string v = k.ToString("c", us);
Console.WriteLine(v); //$12,345.00
CultureInfo dk = new CultureInfo("da-DK");
string w = k.ToString("c", dk);
Console.WriteLine(w); //kr 12.345,00
日期的格式化
字母 | 格式 | 含义 |
---|---|---|
d | MM/dd/yyyy | ShortDatePattern(短日期模式) |
D | dddd,MMMM dd,yyyy | LongDatePattern(长日期模式) |
f | dddd,MMMM dd,yyyy HH:mm | Full date and time (long date and short time)(全日期和时间模式) |
F | dddd,MMMM dd,yyyy HH:mm:ss | FullDateTimePattern (long date and long time)(长日期和长时间) |
g | MM/dd/yyyy HH:mm | General (short date and short time)(通用模式,短日期和短时间) |
G | MM/dd/yyyy HH:mm:ss | General (short date and long time)(通用模式,短日期和长时间) |
m,M | MMMM dd | MonthDayPattern(月天模式) |
r,R | ddd,dd MMM yyyy,HH':'mm':'ss 'GMT' | RFC1123Pattern (RFC1123模式) |
S | yyyy-MM-dd HH:mm:ss | SortableDateTimePattern (conforms to ISO 8601) using local time(使用本地时间的可排序模式) |
t | HH:mm | ShortTimePattern (短时间模式) |
T | HH:mm:ss | LongTimePattern(长时间模式) |
u | yyyy-MM-dd HH:mm:ss | UniversalSortable-DateTimePattern (conforms to ISO 8601) using universal time(通用可排序模式) |
U | dddd,MMMM dd,yyyy,HH:mm:ss | UniversalSortable-DateTimePattern(通用可排序模式) |
y,Y | MMMM,yyyy | YearMonthPattern(年月模式) |
DateTimeFormatInfo dtfi;
Console.Write("[I]nvariant or [C]urrent Info?: ");
if (Console.Read() == 'I')
dtfi = DateTimeFormatInfo.InvariantInfo;
else
dtfi = DateTimeFormatInfo.CurrentInfo;
DateTime dt = DateTime.Now;
Console.WriteLine(dt.ToString("D", dtfi));
Console.WriteLine(dt.ToString("f", dtfi));
Console.WriteLine(dt.ToString("F", dtfi));
Console.WriteLine(dt.ToString("g", dtfi));
Console.WriteLine(dt.ToString("G", dtfi));
Console.WriteLine(dt.ToString("m", dtfi));
Console.WriteLine(dt.ToString("r", dtfi));
Console.WriteLine(dt.ToString("s", dtfi));
Console.WriteLine(dt.ToString("t", dtfi));
Console.WriteLine(dt.ToString("T", dtfi));
Console.WriteLine(dt.ToString("u", dtfi));
Console.WriteLine(dt.ToString("U", dtfi));
Console.WriteLine(dt.ToString("d", dtfi));
Console.WriteLine(dt.ToString("y", dtfi));
Console.WriteLine(dt.ToString("dd-MMM-yy", dtfi));
通过IFormatProvider,ICustomFormatter,IFormattable自定义格式化标识
在string.Format("{0}+{1}={2}",a,b,a+b)
的执行中,变量a、b、及计算结果(a+b)会自动调用.ToString()方法。
而在string.Format("d = {0:F}", d)
这样带格式化标识的式子中变量d则是是通过 IFormattable 接口 调用方法 string ToString(string format,IFormatProvider formatProvider)
接口定义
// 提供用于检索控制格式化的对象的机制。
[ComVisible(true)]
public interface IFormatProvider
{
// 返回一个对象,该对象为指定类型提供格式设置服务。
object GetFormat(Type formatType);
}
// 定义一种方法,它支持自定义设置对象的值的格式。
[ComVisible(true)]
public interface ICustomFormatter
{
// 使用指定的格式和区域性特定格式设置信息将指定对象的值转换为等效的字符串表示形式。
string Format(string format, object arg, IFormatProvider formatProvider);
}
// 提供将对象的值格式化为字符串表示形式的功能。
[ComVisible(true)]
public interface IFormattable
{
string ToString(string format, IFormatProvider formatProvider);
}
简单实现
IFormattable
public class Greeting : IFormattable
{
private string name;
public Greeting(string name)
{
this.name = name;
}
public override string ToString()
{
return this.ToString("CN",null);
}
public string ToString(string format, IFormatProvider provider)
{
if (string.IsNullOrEmpty(format))
return this.ToString();
if (provider == null)
provider = CultureInfo.CurrentCulture;
switch (format.ToUpper())
{
case "CN":
case "TW":
return "你好," + name.ToString();
case "US":
case "GB":
return "Hello," + name.ToString();
case "JP":
return "こんにちは," + name.ToString();
default:
throw new FormatException(string.Format("The {0} format string is not supported.", format));
}
}
}
Greeting greeting = new Greeting("张飞");
Console.WriteLine(string.Format("{0}", greeting));
Console.WriteLine(string.Format("{0:US}", greeting));
Console.WriteLine(string.Format("{0:JP}", greeting));
Console.WriteLine(greeting.ToString("CN", CultureInfo.CurrentCulture));
Console.WriteLine(greeting.ToString("US", CultureInfo.CurrentCulture));
Console.WriteLine(greeting.ToString("JP", CultureInfo.CurrentCulture));
IFormatProvider,ICustomFormatter
通过IFormatProvider来实现自定义格式化参数,相对于IFormattable接口来说更加灵活,因为不必为每个类单独去实现IFormattable接口。
public class MyFormater : ICustomFormatter, IFormatProvider
{
public object GetFormat(Type format)
{
if (format == typeof(ICustomFormatter))
return this;
return null;
}
public string Format(string format, object arg, IFormatProvider provider)
{
if (format == null)
{
if (arg is IFormattable)
return ((IFormattable)arg).ToString(format, provider);
return arg.ToString();
}
else
{
if (format == "MyFormater")
{
return "£:" + arg.ToString();
}
else
{
if (arg is IFormattable)
return ((IFormattable)arg).ToString(format, provider);
return arg.ToString();
}
}
}
}
public static void Test()
{
int i = 100;
string printString;
MyFormater myFormater = new MyFormater();
printString = string.Format(myFormater, "{0}", i);
Console.WriteLine(printString);
printString = string.Format(myFormater, "{0:C}", i);
Console.WriteLine(printString);
printString = string.Format(myFormater, "{0:MyFormater}", i);
Console.WriteLine(printString);
}
总结
通过
String Format(IFormatProvider provider, String format, params object[] args)
方法才能使用自定义的格式化标识
Console.WriteLine(string.Format(myFormater, "{0:MyFormater}", 100)); //£.100
Console.WriteLine(100.ToString("MyFormater", new MyFormater())); //100
Console.WriteLine(string.Format(DateTimeFormatInfo.InvariantInfo, "{0:yy-MM-dd}", DateTime.Parse("2016.12.21"))); //16-12-21
Console.WriteLine(string.Format("{0:yy-MM-dd}", DateTime.Parse("2016.12.21"))); //16-12-21
Console.WriteLine(DateTime.Parse("2016.12.21").ToString("yy-MM-dd", DateTimeFormatInfo.InvariantInfo));//16-12-21
IConvertible
// 定义特定的方法,这些方法将实现引用或值类型的值转换为具有等效值的公共语言运行时类型。
[CLSCompliant(false)]
[ComVisible(true)]
public interface IConvertible
{
// 枚举常数,它是实现该接口的类或值类型的 System.TypeCode。
TypeCode GetTypeCode();
bool ToBoolean(IFormatProvider provider);
byte ToByte(IFormatProvider provider);
char ToChar(IFormatProvider provider);
DateTime ToDateTime(IFormatProvider provider);
decimal ToDecimal(IFormatProvider provider);
double ToDouble(IFormatProvider provider);
short ToInt16(IFormatProvider provider);
int ToInt32(IFormatProvider provider);
long ToInt64(IFormatProvider provider);
sbyte ToSByte(IFormatProvider provider);
float ToSingle(IFormatProvider provider);
string ToString(IFormatProvider provider);
object ToType(Type conversionType, IFormatProvider provider);
ushort ToUInt16(IFormatProvider provider);
uint ToUInt32(IFormatProvider provider);
ulong ToUInt64(IFormatProvider provider);
}
类型转换扩展方法
/// <summary>
/// 提供类型转换
/// </summary>
/// <typeparam name="T">要得到的类型(可以是可选类型)</typeparam>
/// <param name="obj"></param>
/// <param name="convertTo">可以主动提供一个TypeConverter类型,会自动调用它的CanConvertTo和ConvertTo方法</param>
/// <param name="format">格式化标识符</param>
/// <param name="formatProvider">格式化提供器</param>
/// <returns>转换结果</returns>
public static T ConvertTo<T>(this object obj, TypeConverter convertTo = null,string format = null, IFormatProvider formatProvider = null)
{
if (obj == null)
{
return default(T);
}
Type targetType = typeof(T);
//可选类型
if (targetType.IsNullableType())
{
targetType = targetType.GetUnderlyingType();
}
//枚举
if (targetType.IsEnum)
{
return (T)Enum.Parse(targetType, obj.ToString());
}
//guid
if (targetType == typeof(Guid))
{
object o = Guid.Parse(obj.ToString());
return (T)o;
}
//TypeConverter
if (convertTo != null)
{
if (convertTo.CanConvertTo(targetType))
return (T)convertTo.ConvertTo(obj, targetType);
}
//自定义字符串格式化
if(targetType == typeof(string) && format != null&& formatProvider != null)
{
object result = string.Format(formatProvider, "{0:" + format + "}", obj);
return (T)result;
}
//没有明确指定的转换方式,依次尝试各种可能的方式
Type from = obj.GetType();
convertTo = TypeDescriptor.GetConverter(from);
if (convertTo != null && convertTo.CanConvertTo(targetType))
{
return (T)convertTo.ConvertTo(obj, targetType);
}
TypeConverter convertFrom = TypeDescriptor.GetConverter(targetType);
if (convertFrom != null && convertFrom.CanConvertFrom(from))
{
return (T)convertFrom.ConvertFrom(obj);
}
if (formatProvider == null)
{
formatProvider = CultureInfo.InvariantCulture;
}
if (targetType == typeof(string) && format != null)
{
object result = string.Format(formatProvider, "{0:"+ format + "}", obj);
return (T)result;
}
return (T)Convert.ChangeType(obj, targetType, formatProvider);
}