【机房重构】泛型与Datatable

引言

    在七层登录的时候涉及到了实体和泛型。原本三层登录用的是实体,但是七层在三层的基础上加了一个工厂和接口,不经意间用到了泛型,虽然在初学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并没有那么难,找对了思路就通了。一开始是花费了挺长的时间的。敲通一条线之后瞬间就通了。知其然还要知其所以然,多去探索,这样才能学到更多的东西。为以后的学习打好基础





评论 104
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值