C# NPOI数据导出到Excel之反射
之前努力去理解过反射,但是项目中几乎用不到反射,所以对反射理解效果很差。正好最近做了一个类库,功能是将数据导出到Excel,里面用到了反射。我觉得这个是理解反射比较好的案例,所以将此记录下来。
反射理解:反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。
下面的程序功能是将一组数据导出到Excel:List<T>导出到Excel。
这里实体T以下面(StudentEntity)的简单例子来理解:
public class StudentEntity { [Description("姓名")] public string name { get; set; } [Description("年龄")] public int age { get; set; } [Description("地址")] public string address { get; set; } [Description("手机号码")] public string telphone { get; set; } }
最终输出excel表格如下:
分析一下:
在这里动态变化的只有T;
表格第1行是固定的表头信息,是一种合并单元格的形式,合并的列数也就是T的字段数量;
表格第2行信息是不固定的,是根据实体T里面的字段描述来生成的(当然也可以不用按照我这个模式来);
表格第3-5行就是具体的实体数据。
根据上面的分析得到,我们需要从T中获取信息如下:
1.T的字段个数,也就是表格的列数;
2.T的字段描述,也就是第2行显示的名称;
在这里就需要用到反射,当第1行和第2行产生好后,循环遍历List<T>生成数据就可以了。
下面就一步一步来代码实现;
步骤1:新建一个控制台应用程序(也可以建类库、winform程序);
步骤2:右击“引用”,选择“管理NuGet程序包”;在左面的浏览里输入NPOI,选择最新的版本安装就可以了,我这里选择的是“最新稳定版2.3.0”;
步骤3:定义方法:
/// <summary> /// 导出数据到Excel /// </summary> /// <typeparam name="T"></typeparam> /// <param name="data">数据集合</param> /// <param name="head">表头(第一行数据)</param> /// <param name="sheetName">sheet名称</param> static void ExportToExcel<T>(List<T> data, string head, string sheetName) { }
步骤4:创建一个工作簿(表):
static void ExportToExcel<T>(List<T> data, string head, string sheetName) { IWorkbook wb = new HSSFWorkbook(); //设置工作簿的名称 sheetName = string.IsNullOrEmpty(sheetName) ? "sheet1" : sheetName; //创建一个工作簿 ISheet sh = wb.CreateSheet(sheetName); }
把引用添加上:
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
步骤5:设置前2行(表头+抬头)
总体预览:(代码为第9行以下)
1 static void ExportToExcel<T>(List<T> data, string head, string sheetName) 2 { 3 IWorkbook wb = new HSSFWorkbook(); 4 //设置工作簿的名称 5 sheetName = string.IsNullOrEmpty(sheetName) ? "sheet1" : sheetName; 6 //创建一个工作簿 7 ISheet sh = wb.CreateSheet(sheetName); 8 9 //全局索引 10 int gloal_index = 0; 11 System.Reflection.PropertyInfo[] oProps = null; 12 foreach (T en in data) 13 { 14 if (oProps == null) 15 { 16 oProps = ((Type)en.GetType()).GetProperties(); 17 } 18 if (gloal_index == 0) 19 { 20 #region 表头(第1行) 21 //... 22 #endregion 23 24 #region 抬头(第2行) 25 //... 26 #endregion 27 28 gloal_index = 2; 29 } 30 31 #region 这里是List<T>具体内容 32 //... 33 #endregion 34 35 gloal_index++; 36 } 37 38 }
首先通过反射获取到T的信息,其中列数的值就是oProps.Length;
前两行的设置有gloal_index变量来控制,gloal_index为0时,即循环第一次执行时,初始化前两行数据,然后置gloal_index的值为2,即正常处理List<T>的数据。
5.1 表头里的代码:
#region 表头(第1行) //合并单元格 sh.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 0, 0, oProps.Length - 1)); //创建第1行 IRow row0 = sh.CreateRow(0); //设置第1行高度 row0.Height = 20 * 20; //创建第1行第1列 ICell icell1top0 = row0.CreateCell(0); //设置第1行第1列格式 icell1top0.CellStyle = Getcellstyle(wb, "head"); //设置第1行第1列内容 icell1top0.SetCellValue(head); #endregion
5.2 抬头(第2行)的代码:
#region 抬头(第2行) //创建第2行 IRow row1 = sh.CreateRow(1); //设置高度 row1.Height = 20 * 20; //columnt_index是列的索引 int columnt_index = 0; foreach (System.Reflection.PropertyInfo item in oProps) { //获取T的字段名称 string name = item.Name; //获取T的字段名称的描述 string des = ((DescriptionAttribute)Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute))).Description; //创建第2行的第columnt_index列 ICell icell1top = row1.CreateCell(columnt_index); //设置第2行的第columnt_index列的格式 icell1top.CellStyle = Getcellstyle(wb, ""); //设置第2行的第columnt_index列的内容 if (!string.IsNullOrEmpty(des)) { cell1top.SetCellValue(des); } else { icell1top.SetCellValue(name); } //设置第2行的第columnt_index列的宽度 sh.SetColumnWidth(columnt_index, (int)((15 + 0.72) * 256)); columnt_index++; } #endregion
步骤6:设置主体内容(除前两行外)
#region 这里是List<T>具体内容 //创建第gloal_index行 IRow row_zs = sh.CreateRow(gloal_index); int column_index = 0; foreach (System.Reflection.PropertyInfo pi in oProps) { //创建第gloal_index行的第columnt_index列 ICell icell1top = row_zs.CreateCell(column_index); //设置第gloal_index行的第columnt_index列格式 icell1top.CellStyle = Getcellstyle(wb, ""); //获取en字段值 string v_value = pi.GetValue(en, null) == null ? "" : pi.GetValue(en, null).ToString(); //设置第gloal_index行的第columnt_index列的内容 icell1top.SetCellValue(v_value); column_index++; } #endregion
步骤7:输出数据
//输出内容 using (FileStream stm = File.OpenWrite(@"D:\studentInfo.xls")) { wb.Write(stm); }
格式设置方法Getcellstyle如下:
/// <summary> /// 格式设置 /// </summary> static ICellStyle Getcellstyle(IWorkbook wb, string type) { ICellStyle cellStyle = wb.CreateCellStyle(); //定义字体 IFont font = wb.CreateFont(); font.FontName = "微软雅黑"; //水平对齐 cellStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left; //垂直对齐 cellStyle.VerticalAlignment = VerticalAlignment.Center; //自动换行 cellStyle.WrapText = true; //缩进 cellStyle.Indention = 0; switch (type) { case "head": cellStyle.SetFont(font); cellStyle.Alignment = HorizontalAlignment.Center; break; default: cellStyle.SetFont(font); break; } return cellStyle; }
步骤8:以T为StudentEntity为例生成测试数据:
static void Main(string[] args) { StudentEntity se1 = new StudentEntity() { name = "张三", age = 20, address = "上海", telphone = "16278171615" }; StudentEntity se2 = new StudentEntity() { name = "李四", age = 18, address = "北京", telphone = "19278187590" }; StudentEntity se3 = new StudentEntity() { name = "王五", age = 19, address = "广州", telphone = "18278187590" }; List<StudentEntity> selist = new List<StudentEntity>(); selist.Add(se1); selist.Add(se2); selist.Add(se3); ExportToExcel<StudentEntity>(selist, "学生信息", "学生信息表"); Console.WriteLine("ok"); Console.Read(); }
完整代码如下:
using NPOI.HSSF.UserModel; using NPOI.HSSF.Util; using NPOI.SS.UserModel; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExcelReflection { class Program { static void Main(string[] args) { StudentEntity se1 = new StudentEntity() { name = "张三", age = 20, address = "上海", telphone = "16278171615" }; StudentEntity se2 = new StudentEntity() { name = "李四", age = 18, address = "北京", telphone = "19278187590" }; StudentEntity se3 = new StudentEntity() { name = "王五", age = 19, address = "广州", telphone = "18278187590" }; List<StudentEntity> selist = new List<StudentEntity>(); selist.Add(se1); selist.Add(se2); selist.Add(se3); ExportToExcel<StudentEntity>(selist, "学生信息", "学生信息表"); Console.WriteLine("ok"); Console.Read(); } /// <summary> /// 导出数据到Excel /// </summary> /// <typeparam name="T"></typeparam> /// <param name="data">数据集合</param> /// <param name="head">表头(第一行数据)</param> /// <param name="sheetName">sheet名称</param> static void ExportToExcel<T>(List<T> data, string head, string sheetName) { IWorkbook wb = new HSSFWorkbook(); //设置工作簿的名称 sheetName = string.IsNullOrEmpty(sheetName) ? "sheet1" : sheetName; //创建一个工作簿 ISheet sh = wb.CreateSheet(sheetName); //全局索引 int gloal_index = 0; System.Reflection.PropertyInfo[] oProps = null; foreach (T en in data) { if (oProps == null) { oProps = ((Type)en.GetType()).GetProperties(); } if (gloal_index == 0) { #region 表头(第1行) //合并单元格 sh.AddMergedRegion(new NPOI.SS.Util.CellRangeAddress(0, 0, 0, oProps.Length - 1)); //创建第1行 IRow row0 = sh.CreateRow(0); //设置第1行高度 row0.Height = 20 * 20; //创建第1行第1列 ICell icell1top0 = row0.CreateCell(0); //设置第1行第1列格式 icell1top0.CellStyle = Getcellstyle(wb, "head"); //设置第1行第1列内容 icell1top0.SetCellValue(head); #endregion #region 抬头(第2行) //创建第2行 IRow row1 = sh.CreateRow(1); //设置高度 row1.Height = 20 * 20; //columnt_index是列的索引 int columnt_index = 0; foreach (System.Reflection.PropertyInfo item in oProps) { //获取T的字段名称 string name = item.Name; //获取T的字段名称的描述 string des = ((DescriptionAttribute)Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute))).Description; //创建第2行的第columnt_index列 ICell icell1top = row1.CreateCell(columnt_index); //设置第2行的第columnt_index列的格式 icell1top.CellStyle = Getcellstyle(wb, ""); //设置第2行的第columnt_index列的内容 if (!string.IsNullOrEmpty(des)) { icell1top.SetCellValue(des); } else { icell1top.SetCellValue(name); } //设置第2行的第columnt_index列的宽度 sh.SetColumnWidth(columnt_index, (int)((15 + 0.72) * 256)); columnt_index++; } #endregion gloal_index = 2; } #region 这里是List<T>具体内容 //创建第gloal_index行 IRow row_zs = sh.CreateRow(gloal_index); int column_index = 0; foreach (System.Reflection.PropertyInfo pi in oProps) { //创建第gloal_index行的第columnt_index列 ICell icell1top = row_zs.CreateCell(column_index); //设置第gloal_index行的第columnt_index列格式 icell1top.CellStyle = Getcellstyle(wb, ""); //获取en字段值 string v_value = pi.GetValue(en, null) == null ? "" : pi.GetValue(en, null).ToString(); //设置第gloal_index行的第columnt_index列的内容 icell1top.SetCellValue(v_value); column_index++; } #endregion gloal_index++; } //输出内容 using (FileStream stm = File.OpenWrite(@"D:\studentInfo.xls")) { wb.Write(stm); } } /// <summary> /// 格式设置 /// </summary> static ICellStyle Getcellstyle(IWorkbook wb, string type) { ICellStyle cellStyle = wb.CreateCellStyle(); //定义字体 IFont font = wb.CreateFont(); font.FontName = "微软雅黑"; //水平对齐 cellStyle.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left; //垂直对齐 cellStyle.VerticalAlignment = VerticalAlignment.Center; //自动换行 cellStyle.WrapText = true; //缩进 cellStyle.Indention = 0; switch (type) { case "head": cellStyle.SetFont(font); cellStyle.Alignment = HorizontalAlignment.Center; break; default: cellStyle.SetFont(font); break; } return cellStyle; } } }
以上只是反射很小的一个应用,个人觉得对理解反射比较有帮助。
以上代码可能还有需要改进之处,欢迎批评改正。