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);
}
