相信大家在学习c#的时候,经常会看到IEnumerable、IEnumerator这样的接口或返回类型,在写代码时经常会对数组或List集合进行遍历。那IEnumerable和IEnumerator是干什么的呢?有什么区别?数组和List集合为什么可以遍历而其他某些类型或对象不能遍历?它们之间有什么联系呢?
针对这么多疑问,我本来是想来写一篇文章来记录下的,但我在网上又发现了一篇写的很好的文章,对它们之间的关系讲的很详细,我觉得就算我写的话,不见得写的比这篇文章好 :-)。以下是文章链接 http://blog.csdn.net/byondocean/article/details/6871881 推荐看,不然会影响对后面代码的理解,(老鸟忽略)当然了,这些都不是本文的重点,就如文章的标题,我想写的是 ”由IEnumerable和IEnumerator的延伸“,。。。好吧,我们正式进入主题。
比如我们在开发某个功能时,页面查询的数据量比较大,并且关联的表也很多, 这个时候写sql语句无疑是首选,但我们为了便于操作,还要把DataTable遍历转换成List,当然你会说,一般ORM框架不是支持原生的SQL语句吗?比如EF的SqlQuery,它直接就返回了可枚举的对象了。是的,但OMR框架再怎么封装,最终也是通过ADO.NET方式访问数据库的。
我们通过查询得到了一个DataTable数据集合,这个时候我们让它返回List,我们希望的调用方式如下:
var data = oh.excuteQuery(strSql).FillObjects<Person>().ToList();
传入一个Person 返回 List<Person >非常简洁的调用,像Linq to objects 般流畅,接下来我们就来实现它吧。
1 public static class DataTableConvertToList 2 { 3 //使用静态方法扩展datatable 4 public static IEnumerable<T> FillObjects<T>(this DataTable dataTable) where T : class 5 { 6 return new DataTableEnumerable<T>() { Data = dataTable }; 7 } 8 }
1 public class DataTableEnumerable<T> : IEnumerable<T> 2 { 3 public DataTable Data; 4 5 public IEnumerator<T> GetEnumerator() 6 { 7 return new DataTableEnumerator<T>(Data); 8 } 9 10 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 11 { 12 return new DataTableEnumerator<T>(Data); 13 } 14 15 }
1 public class DataTableEnumerator<T> : IEnumerator<T> 2 { 3 private DataTable data; 4 private int index = -1; 5 6 public DataTableEnumerator(DataTable data) 7 { 8 if (data == null) 9 { 10 data = new DataTable(); 11 } 12 this.data = data; 13 } 14 15 public T Current 16 { 17 get { return convert(); } 18 } 19 20 public void Dispose() { } 21 22 object System.Collections.IEnumerator.Current 23 { 24 get { return convert(); } 25 } 26 27 public bool MoveNext() 28 { 29 index++; 30 return index < data.Rows.Count; 31 } 32 33 public void Reset() 34 { 35 index = -1; 36 } 37 38 private T convert() 39 { 40 var row = data.Rows[index]; 41 42 var tType = typeof(T); 43 44 var properties = tType.GetProperties(); 45 46 //反射动态调用这个类 47 var obj = tType.GetConstructor(new Type[] { }).Invoke(null); 48 49 foreach (DataColumn col in data.Columns) 50 { 51 var val = row[col]; 52 if (val.GetType() == typeof(DBNull)) 53 { 54 continue; 55 } 56 var prop = properties.SingleOrDefault(m => m.Name.ToUpper() == col.ColumnName.ToUpper()); 57 if (prop == null) 58 { 59 continue; 60 } 61 if (prop.PropertyType.IsGenericType && prop.PropertyType.Name == typeof(Nullable<>).Name) 62 { 63 prop.SetValue(obj,Convert.ChangeType(val, prop.PropertyType.GetGenericArguments()[0]), null); 64 } 65 else 66 { 67 68 prop.SetValue(obj, Convert.ChangeType(val, prop.PropertyType), null); 69 } 70 } 71 72 return (T)obj; 73 } 74 } 75
最后我们来看下效果:
上面总的框架是通过继承IEnumerable和IEnumerator 接口来实现迭代的,最后的绑定是通过反射实现的。