引言
在七层登录的时候涉及到了实体和泛型。原本三层登录用的是实体,但是七层在三层的基础上加了一个工厂和接口,不经意间用到了泛型,虽然在初学C#的时候学过,但是一直是懵懵懂懂的
什么是泛型
我们在编写程序的时候,经常遇到两个模块的功能非常相似,只是一个是处理int类型的数据,另一个是处理string类型的数据,或者其他自定义的数据类型,但是我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同。有没有一种办法,在方法中传入通用的数据类型,这样不就可以合并代码了吗?
泛型的出现就是专门解决这个问题的
为什么要用泛型
为了了解这个问题,我们先看下面的代码,代码省略了一些内容,但功能是实现一个栈,这个栈只能处理int数据类型
public class Stack
{
private int[] m_item;
public int Pop(){...}
public void Push(int item){...}
public Stack(int i)
{
this.m_item = new int[i];
}
}
上面代码运行的很好,但是,当我们需要一个展来保存string类型时,该怎么办?很多人都会想到把上面的代码复制一份,把int改成string。当然,这样做本身是没有什么问题的,但是一个优秀的程序员是不会这样做的,因为他以后再需要其他类型的栈该怎样做?还要再复制吗?优秀的程序员会徐昂到用一个通用的数据类型object来实现这个栈
public class Stack
{
private object[] m_item;
public object Pop(){...}
public void Push(object item){...}
public Stack(int i)
{
this.m_item = new[i];
}
}
这个栈写的不错,非常灵活,可以接收任何数据类型。但全面的讲,也不是没有缺陷。主要表现在:当Stack处理值类型时,会出现装箱、拆箱操作,这将在托管堆上分配和回收大量的变量,若数据量大,则性能损失非常严重。在处理引用类型时,虽然没有装箱和折箱操作,但将用到数据类型的强制转换操作,增加处理器的负担。
针对object类型栈的问题,我们引入泛型。泛型用一个通过的数据类型T来代替object,在类实例化指定T的类型,运行时自动编译为本地代码,运行效率和代码质量都有很大提高,并且保证数据类型安全
下面用泛型来重写上面的栈,用一个通用呃数据类型T来作为一个占位符,等待在实例化时用一个实际的类型来代替
public class Stack
{
private T[] m_item;
public T Pop(){...}
public void Push(T item){...}
public Stack(int i)
{
this.m_item = new T[i];
}
}
//类的写法不变,只是引入了通用数据类型T就可以适用于任何数据类型,并且类型安全
//实例化只能保存int类型的类
Stack
a = new Stack
(100);
a.Push(10);
a.Push("8888"); //这一行编译不通过,因为类a只接收int类型的数据
int x = a.Pop();
//实例化只能保存string类型的类
Stack
b = new Stack
(100); b.Push(10); //这一行编译不通过,因为类b只接收string类型的数据 b.Push("8888"); string y = b.Pop();
这个类和object实现的类的区别:
1、类型安全。实例化了int类型的栈,就不能处理string类型的数据,其他数据类型也是一样
2、无需拆箱、装箱。这个类在实例化时,按照所传入的数据类型生成本地代码,本地代码数据类型已确定,所以无需拆箱、装箱
3、无需类型转换
Datatable
DataTable是一个临时保存数据的网格虚拟表(表示内存中数据的一个表)。它无需代码就可以简单的绑定数据库。DataTable表示内存中数据的一个表。 我们知道数据库中存储的是实体表,实体表中有一系列的数据。而DataTable即存储在内存中的表,在持久化到数据库之前,是不会对数据库产生影响的
在三层的时候,没有加sqlhelper之前返回值类型是实体,加上sqlhelper之后返回值类型变成了DataTable。
DataTable有什么缺点呢?
实体类中的属性和数据库表中的字段是相对应的,因此实体类的属性名必须和数据库表中的字段名一模一样。而这恰好违反了面向对象编程的思想
Datatable转List
public class ConvertToList
{
///
/// DataTable转换为List
///
///
实体对象
/// datatable表
///
返回list集合
public List
TableToList
(DataTable dt) where T :new()
{
//定义集合
List
list = new List
();
//获得此模型的类型
Type type = typeof(T);
//定义一个临时变量
string tempName = string.Empty;
//遍历Datatable中所有的数据行
foreach(DataRow dr in dt.Rows)
{
T t = new T();
//获得此模型的公共属性
PropertyInfo[] propertys = t.GetType().GetProperties();
//遍历该对象的所有属性
foreach(PropertyInfo pi in propertys)
{
//将属性名称赋值给临时变量
tempName = pi.Name;
//检查DataTable是否包含此列(列名==对象的属性名)
if(dt.Columns.Contains(tempName))
{
//判断此属性是否有Setter
if (!pi.CanWrite) continue;//该属性不可写,直接跳出
//取值
object value = dr[tempName];
//如果非空,则赋给对象的属性
if (value != DBNull.Value)
pi.SetValue(t, value, null);
}
}
//对象添加到泛型集合中
list.Add(t);
}
return list;
}
}
上面这个由DataTable转List的方法是通用的,那么如何调用呢?写在D层的方法
public IList
GetByName(string name)
{
//定义一个对象
IList
user = null;
//为语句构造参数
SqlParameter p = new SqlParameter("@name", name);
//构造语句
string sql = "select * from User_Info where userid = @name ";
//为语句构造参数
//SqlParameter p = new SqlParameter("@name",name);
//执行查询得到结果
DataTable dt = new DataTable();
dt = SqlDbHelper.GetDataTable(sql, p);
//判断是否根据用户名查找到了对象
if(dt.Rows.Count>0)
{
//用户名是存在的
//user = new UserInfo()
// {
// userNo = (Convert.ToInt32(dt.Rows[0][0])),
// UserID = name,
// Pwd = (dt.Rows[0][2].ToString()).Trim(),
// Level = (Convert.ToString(dt.Rows[0][3])),
// UserName = (Convert.ToString(dt.Rows[0][4])),
// AccountNo = (Convert.ToString(dt.Rows[0][5]))
// };
//用户名是存在的
ConvertToList ctl = new ConvertToList();
List
list=new List
();
list = ctl.TableToList
(dt); return list; } else { //用户名不存在 } return user; }
结语
DataTable转List并没有那么难,找对了思路就通了。一开始是花费了挺长的时间的。敲通一条线之后瞬间就通了。知其然还要知其所以然,多去探索,这样才能学到更多的东西。为以后的学习打好基础