學生時代,老師常說,好記性不如爛筆頭,事實上確實如此,有些知識你在學習的時候確實倒背如流,可是時間一長又不經常使用了,可能就生疏了,甚至下次有機會使用到的時候,還須要上網查找資料,因此,還不如經常摘錄下來,即便下次忘記具體細節還能從我本身的博客中輕易的找出來呢,還能和各位園友分享知識,還有一點就是,讀書是一件鍥而不捨的事情,我大學期間試過從圖書館借回來的書,三個月限期已到了還沒讀完又還回去了,說到底就是沒有讀書的動力,因此開一個讀書筆記的文章系列也是頗有必要的,督促本身要把這本書啃完。web
建議2:使用默認轉型方法ide
1、string str1 = "str1" + 9; 2、string str2 = "str2" + 9.ToString();
從IL代碼得知,第一行代碼會產生裝箱行爲,而第二行代碼9.ToString()並無發生裝箱行爲,它是經過直接操做內存來完成int到string的轉換,效率要比裝箱高,因此,在使用其餘值類型到字符串的轉換來完成拼接時,避免使用「+」來完成,而應該使用FCL提供的ToString()方法進行類型轉換再拼接;另外,因爲System.String類對象的不可變特性,進行字符串拼接時都要爲該新對象分配新的內存空間,因此在大量字符串拼接的場合建議使用StringBuilder。
int i = 0; float j = 0; j = i; //int到float存在隱式轉換 i = (int)j; //float到int須要顯式轉換
自定義類型經過重載轉換運算符來實現這一類的轉換:
class program { static void main(string[] args) { Ip ip = "127.0.0.1"; //經過Ip類的重載轉換運算符,實現字符串到Ip類型的隱式轉換 Console.WriteLine(ip.ToString()); } } public class Ip : Object { IPAddress value; //構造函數 public Ip(string ip) { value = IPAddress.Parse(ip); } //重載轉換運算符,implicit 關鍵字用於聲明隱式的用戶定義類型轉換運算符。 public static implicit operator Ip(string ip) { Ip iptemp = new Ip(ip); return iptemp; } //重寫基類ToString方法 public override string ToString() { return value.ToString(); } }
二、使用類型內置的Parse
在FCL中,類型自身會帶有一些轉換方法,好比int自己提供Parse、TryParse方法……
三、使用幫助類提供的方法
System.Convert提供將一個基元類型轉換到其餘基元類型的方法,如ToChar、ToBoolean等,若是是自定義類型轉換爲任何基元類型,只要自定義類型實現IConvertible接口而且實現相關的轉換方法便可;
ps:基元類型是指編譯器直接支持的數據類型,即直接映射到FCL中的類型,包括sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、bool、decimal、object、string。
四、CLR支持的轉換
即子類與父類的上溯轉換和下溯轉換;子類向父類轉換的時候,支持隱式轉換,而當父類向子類轉換的時候,必須是顯式轉換,就比如,狗(子類)是動物(父類),但,動物不必定是狗,也多是貓。
secondType = (SecondType)firstType;
以上代碼發生強轉換類型,意味着下面兩種事情的其中一件;
1)FirstType和SecondType彼此依靠轉換操做符來完成兩個類型的轉換;
2)FirstType是SecondType的基類;
第一種狀況:FirstType和SecondType存在轉換操做符
public class FirstType { public string Name { get; set; } } public class SecondType { public string Name { get; set; } //explicit 和 implicit 屬於轉換運算符,explicti:顯式轉換,implicit能夠隱式轉換 public static explicit operator SecondType(FirstType firstType) { SecondType secondType = new SecondType() { Name = firstType.Name }; return secondType; } }
這種狀況,必須使用強轉換,而不能使用as操做符
咱們再看看這種狀況,這段代碼編譯成功,可是運行時報錯,其緣由是萬類都繼承自object,可是編譯器會檢查o在運行時是否是SecondType類型,從而繞過了轉換運算符,因此建議,若是類型之間存在繼承關係,首選使用as,子類之間的轉換應該提供轉換運算符以便進行強制轉換。
第二種狀況:FirstType是SecondType的基類
這種狀況,既可使用as也可使用強制轉換,從效率和代碼健壯性來看,建議使用as,由於as操做符不會拋出異常,類型不匹配的時候,返回值爲null。
is和as:
object o = new object(); if (o is SecondType) { secondType = (SecondType)o; }
這段代碼實際效率不高,由於執行了2次類型檢測,is操做符返回boolean返回值,只是檢測並無轉換,而as操做符會進行轉換,若是轉換失敗則返回null;
//Parse int a = int.Parse("123"); //TryParse int x = 0; if (int.TryParse("123", out x)) { //轉換成功,x=123 } else { //轉換失敗,x=0 }
public struct Nullable<T> where T: struct
可是結構體Struct是值類型,應該也不能爲空纔對啊,書中也沒有解釋得很深刻,很模糊的一兩句就帶過了,因而我繼續深刻探討,首先使用Reflector對mscorlib.dll反編譯;
public struct Nullable<T> where T: struct { private bool hasValue; internal T value; public Nullable(T value); public bool HasValue { get; } public T Value { get; } public T GetValueOrDefault(); public T GetValueOrDefault(T defaultValue); public override bool Equals(object other); public override int GetHashCode(); public override string ToString(); public static implicit operator T?(T value); public static explicit operator T(T? value); }
不知道什麼緣由,當我展開這些方法的時候,都是空空的,可是,我發現它有重載轉換運算符,implicit 是隱式轉換,explicit 是顯式轉換
而後在寫一個小程序,代碼以下:
protected void Page_Load(object sender, EventArgs e) { Nullable<int> a = null; }
而後對這個web應用程序進行反編譯查看:
protected void Page_Load(object sender, EventArgs e) { int? a = new int?(); }
能夠看出,Nullable<int> a = null; 最終是進行了初始化,而此時,hasValue屬性的值也應該爲False;
因此,我猜測,Nullable<int> 或者 int? ……等可空的基元類型設置爲null的時候,實際上並非像引用類型那樣爲null了,而是進行了初始化,而且hasValue屬性的值爲False。
猜測完以後,我去MSDN搜了一下,獲得驗證:http://msdn.microsoft.com/zh-cn/library/ms131346(v=vs.100).aspx
namespace ClassLibrary { public class Person { public const int height = 100; public readonly static int weight = 100; } }
二、在主程序中添加ClassLibrary類庫的引用,輸出常量:
protected void Page_Load(object sender, EventArgs e) { Response.Write("身高:" + ClassLibrary.Person.height); Response.Write("體重:" + ClassLibrary.Person.weight); }
此時毫無疑問的,輸出結果爲:身高:100體重:100,
三、修改Person類中的height、weight常量爲:170,,而且編譯該類庫(注意:只生成該類庫,而不生成主程序)
此時再運行主程序頁面,輸出結果爲:身高:100體重:170 ;
究其緣由,height爲const常量,在第一次編譯期間就已經將值100HardCode在主程序中了,而第二次修改值以後,並無生成主程序,因此,再次運行的時候,仍是第一次的值,咱們使用ILDASM來看看編譯後的IL代碼吧。
enum Week { Money = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7 }
萬一你一不當心代碼寫成這樣
static Week week; protected void Page_Load(object sender, EventArgs e) { Response.Write(week); }
輸出的結果爲0,就會讓人以爲是多了第八個值出來了,因此,建議使用0值做爲枚舉的默認值。
enum Week { Money = 1, Tuesday = 2, TempValue, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7 }
此時,TempValue的值是什麼呢?
Week week = Week.TempValue;
Response.Write(week);
Response.Write(week==Week.Wednesday);
ValueTemp的結果倒是:Wednesday True;
若是沒有爲元素顯式賦值,編譯器會逐個爲元素的值+1,也就是自動在Tuesday=2的基礎上+1,最終TempValue和Wednesday的值都是3,而後做者的意願是但願乾脆就不要指定值了,由於編譯器會自動幫咱們+1,可是,個人想法是,若是不指定值的話,當咱們下次來看看這個枚舉的話,難道要數一數該元素排行第幾才能知道表明的Value嗎?並且,萬一枚舉有修改的話就有可能不當心修改而致使Value亂掉的狀況了。
System.FlagsAttribute屬性
當一個枚舉指定了System.FlagsAttribute屬性以後,就意味着能夠對這些值進行AND、OR、NOT、XOR按位運算,這就要求枚舉中的每一個元素的值都是2的n次冪指數了,其目的是任意個元素想加以後的值都不會和目前枚舉中的任一元素的值相同,書中關於這方面說得不多,只是提了個大概,因而我參考了些資料,作了個DEMO更加深刻的研究。
[Flags] enum Week { None = 0x0, Money = 0x1, Tuesday = 0x2, Wednesday = 0x4, Thursday = 0x8, Friday = 0x10, Saturday = 0x20, Sunday = 0x40 } protected void Page_Load(object sender, EventArgs e) { //利用「|」運算,將各個元素組合起來 Week week = Week.Sunday | Week.Tuesday | Week.Thursday; Response.Write(GetDayOfWeek(week)); } private string GetDayOfWeek(Week week) { string temp = string.Empty; foreach (Week w in Enum.GetValues(typeof(Week))) { //利用「&」運算拆分 if ((week & w) > 0) temp += string.Format("{0} <br>", w.ToString()); } return temp; }
輸出結果爲:
Tuesday
Thursday
Sunday
這種設計是利用了計算機基礎中的二進制數的「與」「或」運算,從而能夠巧妙的將各個元素組合起來成爲一個數據,而且能最後拆分出來,這種設計思想能夠普遍的應用在權限設計、收費方式……等須要多種數據組合的地方。
我再說說其中的原理吧,首先看我定義枚舉的值,對應出來的二進制數爲:
000一、00十、0100、1000 ……
舉個例子:好比0x1和0x8組合,對應的二進制數是:000一、1000,那麼他們經過「|」運算組合起來以後的值是:1001,
也就是調用GetDayOfWeek方法的時候,參數值爲1001了,而後遍歷枚舉的時候進行&運算拆分
Monday:1001 & 0001 = 0001 結果大於0,符合條件
Tuesday:1001 & 0010 = 0000 結果等於0,不符合條件
Wednesday: 1001 & 0100 = 0000 結果等於0,不符合條件
Thursday: 1001 & 1000 = 1000 結果大於0,符合條件
因而,經過這種方法,就能找出當初組合起來的2個元素了。
class Salary { public int RMB { get; set; } public static Salary operator +(Salary s1, Salary s2) { s2.RMB += s1.RMB; return s2; } }
進行重載以後,就能夠這樣使用了,方便多了。
Salary s1 = new Salary() { RMB = 10 }; Salary s2 = new Salary() { RMB = 20 }; Salary s3 = s1 + s2;
class Salary { public string Name { get; set; } public int BaseSalary { get; set; } public int Bonus { get; set; } }
由於ArrayList有sort()這個排序方法,那豈不是不用實現也能進行對比排序了嗎?事實果然如此的美好嗎?
ArrayList companySalary = new ArrayList(); companySalary.Add(new Salary() { Name = "A", BaseSalary = 2000 }); companySalary.Add(new Salary() { Name = "B", BaseSalary = 1000 });
companySalary.Add(new Salary() { Name = "C", BaseSalary = 3000 }); companySalary.Sort(); //排序 foreach (Salary item in companySalary) { Response.Write(item.Name + ":" + item.BaseSalary); }
現實卻如此悲慘,由於對象類裏面有不少字段,編譯器不會智能到知道你要使用哪一個字段來做爲排序對比的字段的。
so,咱們必須對Salary類實現IComparable接口,而且實現接口成員CompareTo(object obj)
class Salary : IComparable { public string Name { get; set; } public int BaseSalary { get; set; } public int Bonus { get; set; } //實現IComparable接口的CompareTo方法,比較器的原理 public int CompareTo(object obj) { Salary staff = obj as Salary; if (BaseSalary > staff.BaseSalary) { return 1; //若是自身比較大,返回1 } else if (BaseSalary == staff.BaseSalary) { return 0; } else { return -1;//若是自身比較小,返回1 } } }
調用地方的代碼不用修改,程序再次跑起來,運行結果爲:
B:1000 A:2000 C:3000
OK,咱們再次深刻一點,假設這個月結算不以BaseSalary來排序,而是以Bonus獎金來排序,那該怎麼辦?固然,從新修改Salary類內部的CompareTo接口成員確定是能夠的,可是,比較聰明的方法就是自定義比較器接口IComparer(注意,剛纔實現接口名字叫IComparable,而自定義的比較器接口是IComparer)
class BonusComparer : IComparer { public int Compare(object x, object y) { Salary s1 = x as Salary; Salary s2 = x as Salary; return s1.Bonus.CompareTo(s2.Bonus); //實際上,上例也可使用內部字段的CompareTo方法 //可是因爲演示比較器內部原理,則寫了幾個if了。 } }
Sort方法接受一個實現了IComparer接口的類對象做爲參數,因此,咱們能夠這樣子進行傳參
//提供非默認的比較器BonusComparer companySalary.Sort(new BonusComparer());
關於比較器的內容,書中說到這裏就應該結束了,接下來是考慮比較的時候性能的問題,能夠想象,若是一個集合成千上萬的數據甚至更多須要比較的話,而上面的例子中,使用了類型轉換Salary s1 = x as Salary;這是很是消耗性能的,泛型的出現,能夠很好的避免類型轉換的問題:
一、ArrayList可使用List<T>來代替
二、使用IComparable<T> 、 IComparer<T> 來代替
Just Look Like That
class Salary : IComparable<Salary> { public string Name { get; set; } public int BaseSalary { get; set; } public int Bonus { get; set; } public int CompareTo(Salary staff) { return BaseSalary.CompareTo(staff.BaseSalary); } } class BonusComparer : IComparer<Salary> { public int Compare(Salary x, Salary y) { return x.Bonus.CompareTo(y.Bonus); } }