c# 你应该知道的List和Dictionary小技巧
List和Dictionary想必是我们平常用到最多的C#容器了,他们使用起来都很简单,所以很多人就可能就没去深究,其实在使用过程中有很多的小技巧能让我们写的代码变得更高效也更安全。1·合理的指定初始容量。List和Dictionary的构造函数都有一个入参为int的构造函数:public Dictionary(int capacity);和public List(int capa
List和Dictionary想必是我们平常用到最多的C#容器了,他们使用起来都很简单,所以很多人就可能就没去深究,其实在使用过程中有很多的小技巧能让我们写的代码变得更高效也更安全。
1·合理的指定初始容量。
List和Dictionary的构造函数都有一个入参为int的构造函数:public Dictionary(int capacity);和public List(int capacity);。capacity用来指定List和Dictionary的初始容量。这步操作有什么用呢?是这样的,List和Dictionary的内部实现方式都是使用数组,因为数组的容量是固定的,所以初始化的时候就会对其申请内存,List的默认大小为4,Dictionary的默认大小为3。也就是说如果不指定初始化容量的,系统会给你默认创建。当你往容器里Add数据时,如果当前数组已满,则新创建一块两倍于当前数组长度的内存,把原有数据copy到新内存中,再继续往里添加。例如你创建一个没指定初始容量的List后,依次往里面添加了10个元素,内存是这样分配的:首先默认分配了一块长度为4个List元素的内存给List数组,当添加到第五个时,发现长度已然不足,所以分配4*2=8的内存,把原有的4个数据拷贝到新内存块中并把第五个元素添加到末尾,当添加到第九个时,同第五个的操作一样,所以最终List数组的长度为16,里面存放了10个元素,共分配了三次内存,进行了两次的内存拷贝。讲到这,大家应该就很明了了,合理的指定初始容量,可以减少内存的分配和拷贝次数,甚至还能节省内存空间。
2·给Dictionary添加元素时,建议直接用中括号的方式,而不是使用Add方法。
虽然不管是用[]还是Add方法,调用的都是Dictionary的Insert函数,但是区别在于用Add函数的话,如果已经存在同样的键对值,会直接抛出ArgumentException,这意味着后续的代码都不执行了,这可很要命,明明是一个小问题,可看上去可能是一个大Bug。而用中括号属性的方法,如果不存在会添加,如果存在则进行改写,只要key值不为null,是不会抛出异常的。
3·学会使用List的AsReadOnly函数。
如果你有写C++的经历的话,写C#会觉得蛋疼,因为它不允许入参和返回值设置为const。这意味着别人可以随意修改你的入参和返回值,这好难受啊。List有个返回只读的接口:ReadOnlyCollection<T> AsReadOnly()。首先这个类型名称就叫ReadOnly,起到了强烈的提示作用,其次这个类只有Contains、CopyTo、GetEnumerator、IndexOf几个接口,至少防止了使用者在接口层对List进行修改。
4·利用TryGetValue(TKey key, out TValue value)接口获取Dictionary中的数据。
string GetDictionaryVal(string strkey)
{
if (!dicTest.ContainsKey(strkey))
{
return string.Empty;
}
return dicTest[strkey];
}
上面这段代码其实非常正确,但是从效率的角度上看却对dictionary进行两遍的查找:ContainsKey和下标操作各一次。所以我们可以这么写:
string GetDictionaryVal(string strkey)
{
string strVal = string.Empty;
dicTest.TryGetValue(strkey, out strVal);
return strVal;
}
TryGetValue函数的返回值为bool值,表示是否存在于Dictionary中。
5·编写返回Dictionary或者List的函数时,别返回null,如果没数据可以返回个空的Dictionary或者List,因为如果返回null,上层的代码很容易会对其做foreach时抛出异常。例如:
//纯测试
List<uint> lstInfo = new List<uint>() { 1, 3, 5 };
public List<uint> GetLstInfo(bool bSuc)
{
if (!bSuc)
{
return null;
}
return lstInfo;
}
有些人会写类似于上面这样的接口,然后上层程序员经常会这样使用它:
foreach (var item in GetLstInfo(true))
{
}
如果入参为false,那么将返回null,然后foreach时程序就抛出异常了,后续代码无法执行,所以我们可以这样写:
public List<uint> GetLstInfo(bool bSuc)
{
if (!bSuc)
{
return new List<uint>(0);
}
return lstInfo;
}
为什么参数传0,请会看第一条。
未完待续。。。。
更多推荐
所有评论(0)