C#定義類型轉化 及 格式化字符串

operator 關鍵字

operator 關鍵字用來重載內置運算符,或提供類/結構聲明中的用戶定義轉換。它能夠定義不一樣類型之間採用何種轉化方式和轉化的結果。html

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者均可以調用,編譯器不知道選擇哪一個而出現的錯誤。ide

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);
  1. string.Format方法中的不定參數args是一個數組,而format參數中的形如{0},{1}中的序號則是數組中對應的索引,因此最大序號必須小於參數個數(由於數組不能越界)。
  2. {}是微軟定義好的標記,用於分割format字符串。若是須要在字符串中包含大括號的話就必須進行轉義,這個轉義也和咱們平時使用的"/"轉義表示法不一樣,須要使用兩個大括號進行轉義如 {{ 或者 }},相似於逐字前綴字符@修飾的字符串中雙引號的表示。函數

    var msg2 = string.Format("Hello {{}},I am {0}", "張飛");
     var msg3 = @"張飛""";
  3. string.Format方法內部經過StringBuilder實現字符串的拼接。
  4. 形如"{ N [, M ][: formatString ]}"的格式化表示中:
    1. N是從0開始的整數,表示要格式化的參數的個數
    2. M是一個可選的整數,表示格式化後的參數所佔的寬度,若是M是負數,那麼格式化後的值就是左對齊的,若是M是正數,那麼格式化後的值是右對齊的
    3. formatString爲是另一個可選的參數,表示格式代碼

數字的格式化

標準格式化標識符

數學格式化爲字符串時,格式代碼一般是象‘X0’這樣的格式。X是格式化標識符,0是精度標識符。格式標識符號共有9種,它們表明了大多數經常使用的數字格式。ui

字母 含義
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進制格式

圖形化格式字符串

若是標準格式化標識符還不能知足需求。可使用圖形化格式字符串來建立定製的字符串輸出。this

圖形化格式化使用佔位符來表示最小位數,最大位數,定位符號,負號的外觀以及其它數字符號的外觀。code

符號 名稱 含義
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()方法。orm

而在string.Format("d = {0:F}", d)這樣帶格式化標識的式子中變量d則是是經過 IFormattable 接口 調用方法string ToString(string format,IFormatProvider formatProvider)htm

接口定義

//     提供用於檢索控制格式化的對象的機制。
[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);
}
相關文章
相關標籤/搜索