在Net2.0中String類(lèi)型有一個(gè)靜態(tài)方法IsNullOrEmpty,到了Net4.0中String類(lèi)又增加了一個(gè)新的靜態(tài)方法IsNullOrWhiteSpace。這兩個(gè)方法看名稱(chēng)也可以知道IsNullOrEmpty是判斷空引用和空字符串,而IsNullOrWhiteSpace是判斷空引用和字符串中的每一個(gè)字符是否是空格。
在有這兩個(gè)方法之前,我們要進(jìn)行這樣的判斷,需要些如下代碼
public string GetFileName(string fullPathFileName)
{if (fullPathFileName == null || fullPathFileName.Length == 0)
{throw new ArgumentNullException(fullPathFileName);
} //...
} 使用IsNullOrEmpty
public string GetFileName(string fullPathFileName)
{if (string.IsNullOrEmpty(fullPathFileName))
{ throw new ArgumentNullException(fullPathFileName);
} //...
} 下面又了新的需求,需要將三個(gè)名字連接在一起,并且希望中間名字不為空字符串和不出現多余的空格,我們會(huì )寫(xiě)出下面的代碼
public string GetFullName(string firstName, string middleName, string lastName)
{if (middleName == null || middleName.Trim().Length == 0)
{return string.Format('{0} {1}', firstName, lastName);
}return string.Format('{0} {1} {2}', firstName, middleName, lastName);
} 上面的代碼中使用了Trim來(lái)去掉空格然后判斷其長(cháng)度是否為0,代碼也非常的清晰簡(jiǎn)潔,但是會(huì )產(chǎn)生額外的String對象以至于影響性能,這時(shí)就應該使用Net4.0中的IsNullOrWhiteSpace方法
public string GetFullName(string firstName, string middleName, string lastName)
{if (string.IsNullOrWhiteSpace(middleName))
{return string.Format('{0} {1}', firstName, lastName);
}return string.Format('{0} {1} {2}', firstName, middleName, lastName);
} 上面的代碼非常簡(jiǎn)潔,而且也不用擔心會(huì )產(chǎn)生額外的String對象沒(méi)有及時(shí)的進(jìn)行垃圾回收而影響性能。
string.Equals方法有很多的重載供我們使用,但是其中有些常常會(huì )被我們忽視掉。通常我們比較字符串會(huì )使用下面的方法
public Order CreateOrder(string orderType, string product, int quantity, double price)
{if (orderType.Equals('equity'))
{ }// ...
} 如果orderType為null會(huì )拋出NullReferenceException異常,所以為了不拋出異常,在判斷之前先要進(jìn)行null的判斷,如下:
if (orderType != null && orderType.Equals('equity'))
相當于每次都要做兩次判斷,很麻煩而且有時(shí)還有可能遺忘,如果使用string.Equals就可以解決這個(gè)問(wèn)題,代碼如下:
if (string.Equals(orderType, 'equity'))
上面的代碼當orderType為null時(shí)不會(huì )拋出異常而是直接返回false。
判斷字符串相等的時(shí)候有時(shí)會(huì )需要區分大小寫(xiě),很多人的習慣做法是先轉換成大小或是小些在進(jìn)行比較(建議轉換成大寫(xiě),因為編譯器做了優(yōu)化可以提高性能),但是當轉換成大寫(xiě)或是小寫(xiě)時(shí)又會(huì )創(chuàng )建的的字符串,使性能降低。這時(shí)應該使用StringComparison.InvariantCultureIgnoreCase,代碼如下
if (orderType.Equals('equity', StringComparison.InvariantCultureIgnoreCase))
如果要考慮到null的情況,還是應該使用string.Equal
if (string.Equals(orderType, 'equity', StringComparison.InvariantCultureIgnoreCase))
我們都知道using最常用的地方就是在類(lèi)中引用命名空間。除此之外還可以用作設置別名和應用在一些實(shí)現了IDisposable 借口的對象實(shí)例上,可以使這些對象在using的作用范圍內自動(dòng)釋放資源。下面的代碼示例是沒(méi)有使用using的情況:
public IEnumerable<Order> GetOrders()
{var orders = new List<Order>();
var con = new SqlConnection('some connection string');
var cmd = new SqlCommand('select * from orders', con);
var rs = cmd.ExecuteReader();
while (rs.Read())
{// ...
}
rs.Dispose(); cmd.Dispose(); con.Dispose();return orders;
} 上面的代碼不怎么好看,而且也沒(méi)有對異常的處理,如果在代碼執行過(guò)程中出現了異常將會(huì )導致有些資源不能及時(shí)釋放,盡管最終還是會(huì )被垃圾回收,但還是會(huì )影響性能呢。下面的代碼添加了異常處理
public IEnumerable<Order> GetOrders()
{SqlConnection con = null;
SqlCommand cmd = null;
SqlDataReader rs = null;
var orders = new List<Order>();
try
{
con = new SqlConnection('some connection string');
cmd = new SqlCommand('select * from orders', con);
rs = cmd.ExecuteReader();while (rs.Read())
{// ...
}
}finally
{
rs.Dispose(); cmd.Dispose(); con.Dispose(); }return orders;
}上面的代碼仍然存在不足,如果SqlCommand對象創(chuàng )建失敗或是拋出了異常,rs就會(huì )為null,那么最后在執行rs.Dispose()時(shí)就會(huì )拋出異常,會(huì )導致con.Dispose不能被調用,所以我們應該避免這種情況的發(fā)生
public IEnumerable<Order> GetOrders()
{var orders = new List<Order>();
using (var con = new SqlConnection('some connection string'))
{using (var cmd = new SqlCommand('select * from orders', con))
{using (var rs = cmd.ExecuteReader())
{while (rs.Read())
{// ...
}
} } }return orders;
} 上面的代碼中的using嵌套了好幾層,看起來(lái)很繁瑣,而且可讀性也不是很好,我們可以像下面這樣改進(jìn)
public IEnumerable<Order> GetOrders()
{var orders = new List<Order>();
using (var con = new SqlConnection('some connection string'))
using (var cmd = new SqlCommand('select * from orders', con))
using (var rs = cmd.ExecuteReader())
{while (rs.Read())
{// ...
}
}return orders;
} 很多人在創(chuàng )建類(lèi)的時(shí)候沒(méi)有使用過(guò)Static修飾符,可能他們并不知道Static修飾符的作用,Static修飾符所做的一些限制可以在其他開(kāi)發(fā)人員使用我們代碼的時(shí)候使我們的代碼變得更加安全。比如我們現在寫(xiě)一個(gè)XmlUtility類(lèi),這個(gè)類(lèi)的作用是實(shí)現XML的序列化,代碼如下:
public class XmlUtility
{public string ToXml(object input)
{var xs = new XmlSerializer(input.GetType());
using (var memoryStream = new MemoryStream())
using (var xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding()))
{ xs.Serialize(xmlTextWriter, input);return Encoding.UTF8.GetString(memoryStream.ToArray());
} }} 上面的是很典型的XML序列化代碼,但是我們在使用時(shí)需要先實(shí)例化這個(gè)類(lèi)的對象,然后用對象來(lái)調用方法
var xmlUtil = new XmlUtility();
string result = xmlUtil.ToXml(someObject);
這樣顯然很麻煩,不過(guò)我們可以給方法加上static修飾符,然后給類(lèi)加上私有的構造函數防止類(lèi)實(shí)例化來(lái)使類(lèi)的使用變得簡(jiǎn)單
public class XmlUtility
{private XmlUtility()
{ }public static string ToXml(object input)
{var xs = new XmlSerializer(input.GetType());
using (var memoryStream = new MemoryStream())
using (var xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding()))
{ xs.Serialize(xmlTextWriter, input);return Encoding.UTF8.GetString(memoryStream.ToArray());
} }} 上面的代碼可以實(shí)現類(lèi)直接調用方法,但是給類(lèi)設置私有構造函數的做法不是很好,當我們給類(lèi)誤添加了非靜態(tài)方法時(shí),類(lèi)不能實(shí)例化,添加的非靜態(tài)方法就形同虛設了
public T FromXml<T>(string xml) { ... }
所以我們需要將類(lèi)設置成靜態(tài)的,這樣當類(lèi)中有非靜態(tài)方法時(shí)編譯時(shí)就會(huì )拋出異常,告訴我們類(lèi)中只能包含靜態(tài)成員
public static class XmlUtility
{public static string ToXml(object input)
{var xs = new XmlSerializer(input.GetType());
using (var memoryStream = new MemoryStream())
using (var xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding()))
{ xs.Serialize(xmlTextWriter, input);return Encoding.UTF8.GetString(memoryStream.ToArray());
} }} 給類(lèi)添加Static修飾符,該類(lèi)就只能包含靜態(tài)成員,并且不能被實(shí)例化,我們也不可能隨便就給添加了一個(gè)非靜態(tài)的成員,否則是不能編譯通過(guò)的。
在C#3.0及以上版本中增加了對象和集合初始化器的新特性,會(huì )使代碼看起來(lái)更加簡(jiǎn)潔,還有可能帶來(lái)更高的性能。初始化器其實(shí)就是一個(gè)語(yǔ)法糖??聪旅娴睦?,給出的是一個(gè)結構
public struct Point
{public int X { get; set; }
public int Y { get; set; }
} 普通初始化如下
var startingPoint = new Point();
startingPoint.X = 5;startingPoint.Y = 13;使用初始化器初始化
var startingPoint = new Point() { X = 5, Y = 13 };
代碼的確精簡(jiǎn)了不少,從三行減到了一行,可以讓我們少敲很多字。
下面再來(lái)看看集合的初始化,假設我們在一個(gè)集合List中添加5個(gè)整數
var list = new List<int>();
list.Add(1);list.Add(7);list.Add(13);list.Add(42);使用集合初始化器,代碼如下
var list = new List<int> { 1, 7, 13, 42 };
如果我們事先知道要加載的數量,可以給List設置默認的容量值,如下
var list = new List<int>(4) { 1, 7, 13, 42 };
下面來(lái)看一個(gè)通常情況下對象和集合一起使用的例子
var list = new List<Point>();
var point = new Point();
point.X = 5;point.Y = 13;list.Add(point);point = new Point();
point.X = 42;point.Y = 111;list.Add(point);point = new Point();
point.X = 7;point.Y = 9;list.Add(point); 下面為使用了初始化器的代碼,可以對比一下區別
var list = new List<Point>
{new Point { X = 5, Y = 13 },
new Point { X = 42, Y = 111 },
new Point { X = 7, Y = 9 }
}; 使用對象或集合初始化器給我們帶來(lái)了非常簡(jiǎn)潔的代碼,盡管有時(shí)一條語(yǔ)句會(huì )占用多行,但是可讀性是非常好的。
有些時(shí)候在性能上也會(huì )帶來(lái)提升,看下面兩個(gè)類(lèi)
public class BeforeFieldInit
{public static List<int> ThisList = new List<int>() { 1, 2, 3, 4, 5 };
} public class NotBeforeFieldInit
{public static List<int> ThisList;
static NotBeforeFieldInit()
{ThisList = new List<int>();
ThisList.Add(1); ThisList.Add(2); ThisList.Add(3); ThisList.Add(4); ThisList.Add(5); }} 這兩個(gè)類(lèi)都是做同樣的事情,都是創(chuàng )建一個(gè)靜態(tài)的List字段,然后添加了1到5五個(gè)整數。不同的是第一個(gè)類(lèi)在生成的IL代碼中類(lèi)上會(huì )添加beforefieldinit標記,對比兩個(gè)類(lèi)生成的IL代碼
.class public auto ansi beforefieldinit BeforeFieldInit
extends [mscorlib]System.Object{} // end of class BeforeFieldInit
.class public auto ansi NotBeforeFieldInit
extends [mscorlib]System.Object{} // end of class NotBeforeFieldInit
有關(guān)靜態(tài)構造函數的性能問(wèn)題可以參考CLR Via C# 學(xué)習筆記(5)靜態(tài)構造函數的性能
本文是參考老外系列博客的第二篇寫(xiě)的,并不是直譯,原文見(jiàn)下面鏈接。希望本文對大家有所幫助。
聯(lián)系客服