WSS v3中的对象模型(OM)是一种全新的对象模型,它要处理的数据大多都来自于内容数据库,就是说它首先需要把数据从内容数据库中读入内存后才能进行后续的操作。所以一个好的编程习惯有时可能会左右站点的性能。
我们知道在.net中已经建立了非常完整的方法来迭代集合中的对象,以及如何删除,修改内存中的对象,众所周知迭代的方法分为foreach和forloop方法。
foreach 语法简介:
foreach 语句为数组或对象集合中的每个元素重复一个嵌入语句组。foreach 语句用于循环访问集合以获取所需信息,但不应用于更改集合内容以避免产生不可预知的副作用。嵌入语句为数组或集合中的每个元素继续执行。当为集合中的所有元素完成迭代后,控制传递给 foreach 块之后的下一个语句。
foreach 语法特点:
foreach是只读的循环结构,它在循环时不需要设置名称变量或者是对象属性,要使foreach迭代集合的类型,就要实现IEnumerable接口。枚举集合要包含MoveNext方法,这个方法每次只访问一个对象,这有点像ADO.NET中的SqlDataReader,它只能朝一个方向读取,要么从头开始,要么从末尾开始。它只表示只读数据视图,不支持对象的删除及更新操作。
forloop语法简介:
forloop它的一般形式为: for(<初始化>; <条件表过式>; <增量>) 语句;
1:初始化总是一个赋值语句, 它用来给循环控制变量赋初值;
2:条件表达式是一 个关系表达式, 它决定什么时候退出循环;
3:增量定义循环控制变量每循环一次后 按什么方式变化。
forloop语法特点:
forloop除了能遍历元素外,还支持元素的修改及删除操作。
两者的区别:
通常情况下forloop循环关效率上比foreach要高。
本文内容:这篇文章我用以下两个示例分析foreach和forloop在迭代集合时的性能表现:
1:C#迭代一个包含50000个数字的ArrayList;
2:MOSS中迭代SPWebCollection和SPListItemCollection,分析后台调用存储过程的次数。
第一个示例: 我们可以做这样的一个示例,在一个ArrayList中插入50000个整数,数据大点好查看区别。然后分别通过foreach和forloop的方式把队列中的元素输出到asp.net页面,通过比较所用时间来判断它们在性能上的差别。代码如下:
{
// 构造一个队列包含一千个数
ArrayList list = new ArrayList ();
for ( int i = 0 ;i < 50000 ;i ++ )
{
list.Add(i);
}
// forloop开始时间
DateTime forloopStartTime = DateTime.Now;
for ( int i = 0 ; i < list.Count; i ++ )
{
Response.Write(list[i].ToString());
}
// forloop结束时间
DateTime forloopEndTime = DateTime.Now;
Response.Write( " forloop用时: " + this .DateDiff(forloopEndTime, forloopStartTime));
// foreach开始时间
DateTime foreachStartTime = DateTime.Now;
foreach ( object o in list)
{
Response.Write(o.ToString());
}
// forloop结束时间
DateTime foreachEndTime = DateTime.Now;
Response.Write( " foreach用时: " + this .DateDiff(foreachEndTime, foreachStartTime));
}
///
/// 计算两个日期的时间间隔
///
/// 第一个日期和时间
/// 第二个日期和时间
///
private string DateDiff(DateTime DateTime1, DateTime DateTime2)
{
string dateDiff = null ;
TimeSpan ts1 = new TimeSpan(DateTime1.Ticks);
TimeSpan ts2 = new TimeSpan(DateTime2.Ticks);
TimeSpan ts = ts1.Subtract(ts2).Duration();
dateDiff = ts.Days.ToString() + " 天 "
+ ts.Hours.ToString() + " 小时 "
+ ts.Minutes.ToString() + " 分钟 "
+ ts.Seconds.ToString() + " 秒 "
+ ts.Milliseconds.ToString() + " 毫秒 " ;
return dateDiff;
}
最后页面的输出结果为:
forloop用时:0天0小时0分钟0秒31毫秒
foreach用时:0天0小时0分钟0秒46毫秒
测试结论:在C#中迭代ArrayList,可以非常清楚的看出forloop的效率要高于foreach
疑问:在《SharePoint Services 3.0开发指南 》中提出了一个不同的观点: 通常情况下forloop循环效率上比foreach要高。但这种情况也不是绝对的,起码在WSS中迭代对象是不是这样。迭代集合时,要访问每个项,如果些时站点比较多而且数据库交互同样比较多,那么用传统的forloop方式会使数据库变的越来越慢。比较方法:分别用foreach和forloop循环遍历SPWebConllection,通过监视数据库的访问情况来说明在WSS V3情况下,forloop的效率会低于foreach。(上面的代码并非原文,回家后再重写下,意思差不多)代码如下:
// foreach循环
foreach (SPWeb web in sites.AllWebs)
{
Response.Write(web.Title);
}
// forloop循环
SPWebConllection subSites = sites.AllWebs;
for ( int i = 0 ;i < subSites.Count;i ++ )
{
Response.Write(web[i].Title);
}
分析原理:利用SqlProfiler查看存储过程调用情况Proc_GetWebPartsMetaDataandListMetaData,发现forloop 调用了三次而foreach只调用了一次。
学习过程中产生疑问:做测试时,根本没有看到Proc_GetWebPartsMetaDataandListMetaData这个存储过程,而且也没有出现一次和三次的情况。于是有了第二个测试示例。
第二个测试示例:建两个用户控件:(每个控件中都循环了两种集合:SPWebCollection,SPListItemCollection)
1:CustomList.ascx,专门用来做foreach测试;示例代码:
/// foreach循环SPWebCollection
///
private void foreachSite()
{
SPSite sites = SPControl.GetContextSite(Context);
foreach (SPWeb web in sites.AllWebs)
{
Response.Write(web.Title);
}
}
///
/// foreach循环SPListItemCollection
///
private void foreachList()
{
SPSite site = SPContext.Current.Site;
SPWeb web = site.OpenWeb();
SPList list = web.Lists[ " MyList " ];
SPListItemCollection listItems = list.Items;
// 用foreach循环输出列表项的标题
foreach (SPListItem item in listItems)
{
Response.Write(item[ " sTitle " ].ToString());
}
}
2: CustomList_forloop.ascx,专门用来做forloop测试。示例代码:
/// forloop循环SPWebCollection
///
private void forloopSite()
{
SPSite sites = SPControl.GetContextSite(Context);
SPWebCollection subSites = sites.AllWebs;
for ( int i = 0 ; i < subSites.Count; i ++ )
{
Response.Write(subSites[i].Title);
}
}
///
/// forloop循环SPListItemCollection
///
private void forloopList()
{
SPSite site = SPContext.Current.Site;
SPWeb web = site.OpenWeb();
SPList list = web.Lists[ " MyList " ];
SPListItemCollection listItems = list.Items;
// 用foreach循环输出列表项的标题
for ( int i = 0 ; i < listItems.Count; i ++ )
{
Response.Write(listItems[i].Title.ToString());
}
}
SqlProfiler中的观察结果如下图:
我的测试结论:
1: 根本没有看到Proc_GetWebPartsMetaDataandListMetaData这个存储过程;
2: 没有出现Proc_GetWebPartsMetaDataandListMetaData调用一次和三次的情况。
3:当循环SPWebCollection时,无论是foreach还是forloop都只会调用下面存储过程一次,当两个循环都存在时也只会调用一次(这点好像形成了一个单例,我猜的,如果大家有什么证明的地方可以指教):
1):proc_GetTpWebMetaDataAndListMetaData;
2):proc_ListAllWebsOfSite。
4:当循环SPListItemCollection时,如果只出现foreach和forloop其中的一个,那么会调用以下存储过程一次,如果两个同时存在,则会调用下面存储过程两次:
1):proc_GetTpWebMetaDataAndListMetaData
2):proc_EnumLists
3): proc_GetListMetaDataAndEventReceivers,重点说明,这个存储过程调用的次数等于列表项的数量。
说明:我并没有否认书中观点,只是有点疑问,希望大家帮忙解释下。
总结:针对两个测试结果,我主要在迭代的性能上总结下foreach和forloop两者的区别:
1:通常情况下,forloop的效率比foreach高;
2:当集合的数据量比较小时,两者的性能差别不明显;
3:在WSS v3中迭代集合,后台调用的存储过程的次数在我看来一样,并没有发生书中说的观点:forloop调用次数是foreach调用次数的n(n表示集合的元素数量)倍 。
说明:本人为MOSS开发新手,如果文中有什么不对的地方,希望大家指教。