之前努力去理解过反射,但是项目中几乎用不到反射,所以对反射理解效果很差。正好最近做了一个类库,功能是将数据导出到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;
}
}
}
以上只是反射很小的一个应用,个人觉得对理解反射比较有帮助。
以上代码可能还有需要改进之处,欢迎批评改正。