C#反射与特性使用简介
本文是学习特性与反射的学习笔记,在介绍完特性和反射之后,会使用特性与反射实现一个简单的将DataTable转换为List的功能,水平有限,如有错误,还请大神不吝赐教。
1. 反射:什么是反射?反射就是在程序运行的过程中,动态的获取类的成员,并对他们进行操作。包括动态调用方法,动态获取,设置属性等。通过特性,也能是想IOC,AOP等功能。
2. 特性:特性只有在使用反射的时候才能发挥它最大的作用,通过反射获取到自定义的特性,再根据特性进行操作,例如在通过反射实现ORM的时候,如果一个熟悉设置了不需要ORM的特性,则可以忽略该属性。如果没有反射,那么可以把特性当做注释,它不会对代码的运行造成任何影响。但它和注释的区别在于,它会被编译进程序集,这样才能通过反射获取到这些特性。
3. 下面先演示反射的用法,反射常用的类有Assembly,Activator,Type这几个类
Assembly获取程序集应用,可以通过Load,LoadFile,LoadFrom这几个方法将dll文件加载进当前程序集。如果需要反射的类位于当前程序集,则可以不使用此类
Activator用于动态的创建一个类的实例,通过Assembly加载的程序集,可以获取到它内部的所有的Type,而Activator.Crea teInstance方法可以为当前对象创建一个实例。(Assembly也有CreateInstance方法用于创建类的实例)。
Type表示一个类型,也可以通过typeof获取例如typeof(int)得到的就是int类型,然后Type对象有很多的方法可以获取对象的成员,包括字段,方法,属性等等对象内部的所有都可以通过Type获得。
下面是使用反射的示例
3.1、 添加一个新的类库,添加下面的代码,并生成为一个dll文件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RefleatorDll
{
/// <summary>
/// 此类用于反射获取到的对象的测试类
/// 通过反射获取的类,不需要是public的,即使是private的类也是可以获取到的
/// 类内部的所有成员,即使是private的,全部可以通过反射进行更改,但强烈不建议那样做,私有成员之所以为私有的,一定有必要的原因,
/// 对私有成员的修改可能对类的运行造成难以预料的影响
/// </summary>
public class CustomClass
{
//用于反射的字段,包括私有,公有,静态字段用于演示不同的字段如何修改
//下方的属性相同
private string _name = null;
public int _age = 0;
public static string _address = null;//静态字段不设置属性,如有必要也可以设置
public string Name { get { return _name; } set { _name = value; } }
public int Age { get { return _age; } set { _age = value; } }
//用于反射获取和调用类的方法
//只包括静态和公有方法私有方法的获取请参考私有字段的获取
//方法也包括有返回值与如返回值
//其他情况的方法,请举一反三去获取
public string GetName()
{
return _name;
}
public static void ShowAge(string name,int age)
{
Console.WriteLine($"{name}'s age is :" + age);
}
}
}
3.2、 在主程序中通过反射对此类进行操作
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RefleatorDll.dll");
Assembly asm = Assembly.LoadFile(path);//加载程序集
//asm.GetType方法与GetTypes的区别在于,前者通过指点类型的名称获取指定的Type,后者会获取到当前程序集中的所有类
//类型名使用FullName
Type cs = asm.GetType("RefleatorDll.CustomClass");
//为当前Type创建一个实例
//此方法有很多的重载,可以结合实际情况选择使用哪一个重载
object obj = Activator.CreateInstance(cs);
//获取及修改私有的字段
//如果是通过New的到的对象,私有字段是不能获取及修改的,但是通过反射就可以,同理属性,方法也是一样
//BindingFlags用于设置指定类型的字段,包括私有的,公有的,静态的,继承的,非继承的,等类型
//下面的方法只获取公有的,静态的,非公有的,如果不知道则默认获取公有字段
FieldInfo[] fis = cs.GetFields(BindingFlags.Static|BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance);
FieldInfo fi = cs.GetField("_name", BindingFlags.Instance | BindingFlags.NonPublic);
//如果是实例字段,则需要将上面创建的obj传入,如果是静态字段则传入null即可
Console.WriteLine(fi.GetValue(obj).ToString());//显示当前值,当前显示为""
fi.SetValue(obj, "此坑已满");
Console.WriteLine(fi.GetValue(obj).ToString());//显示当前值,显示为设置的值
Console.WriteLine("方法执行完毕!");
Console.ReadKey();
显示结果为:
属性的操作与字段操作相同只是将FieldInfo换为PropertyInfo即可
下面演示方法的调用
创建对象的方法上面的实例已经有了,所以此处只附调用方法的代码
//带返回值的方法
//如果是重载方法的话,需要在参数2中指定需要使用的方法的参数对应的数量和类型
MethodInfo mis = cs.GetMethod("GetName", BindingFlags.Instance|BindingFlags.Public);
//参数2指定传入的参数数组如果当前执行的方法没有参数则传入null即可
object returnValue = mis.Invoke(obj, null);
Console.WriteLine(returnValue);//由于上面设置了_name的值,所以此处显示设置的值
//静态方法
MethodInfo misS = cs.GetMethod("ShowAge", BindingFlags.Static | BindingFlags.Public);
misS.Invoke(null, new object[] { "此坑已满", 26 });
执行结果
以上就是反射的简单使用方法,下面是反射结合特性的方法
1. 定义一个自定义的特性,.Net框架自带很多的特性,如果写过WCF或者MVC就会有深刻的体验,比[Required],[HttpGet]等
//AttributeUsage特性用于自定义特性,它用于设置特性的作用范围
//例如此特性就只能用在方法上,已经特性是否可以多次使用,是否继承父类的特性等
//自定义特性约定为{name}+Attribute,当然也可以不加Attribute
//区别在于加后在使用时可以不加Attribute否则就必须输入特性的全名
//例如ShowAttribute使用时只需要使用[Show]即可
//自定义特性必须继承自Attribute类
[AttributeUsage(AttributeTargets.Method)]
public class ShowAttribute : Attribute
{
public ShowAttribute(string methodName)
{
Console.WriteLine("您通过特性找到了方法:" + methodName);
}
}
2.定义一个类并添加此特性
//此类用于演示ShowAttribute特性
public class ShowAttTest
{
[Show("ShowMethod")]
public void ShowMethod()
{
//因为此方法只是用来测试特性的功能的,所以不做具体业务处理,直接返回
return;
}
}
3.通过反射调用此方法
//反射特性测试,用于测试静态方法获取自定义特性与使用实例方法获取自定义特性是否执行特性内部方法的差别
Type t = typeof(ShowAttTest);
MethodInfo mi = t.GetMethod("ShowMethod");
//通过反射的实例方法获取自定义特性,此方法会造成特性的代码被执行
mi.GetCustomAttribute(typeof(ShowAttribute));
//通过静态方法获取自定义特性,此方法不会执行特性的代码
//CustomAttributeData.GetCustomAttributes(mi);
运行结果:
以上就是反射和特性的基础知识了,下面使用反射和特性实现一个简单的
将DataTable转换为实体类
获取到的DataTable的数据
转换后的数据请自行调试
示例源码
1. 自定义的特性
//此特性仅用于忽略字段标识,同时设置为只能为属性添加
[AttributeUsage(AttributeTargets.Property)]
public class IngoreAttribute:Attribute
{
}
//此特性用于设置当前属性对应的数据库字段的名称
[AttributeUsage(AttributeTargets.Property)]
public class ColumnNameAttribute : Attribute
{
public string Name;//用于保存字段的名称
public ColumnNameAttribute(string name)
{
Name = name;
}
}
2. 自定义实体
//此类模拟一个实体类
public class CustomEntity
{
public int ID { get; set; }
public string Name { get; set; }
[ColumnName("TelePhone")]//设置Phone的字段为TelePhone
public string Phone { get; set; }
public string Email { get; set; }
[Ingore]//忽略此字段,不从DataTable中获取
public string IngoreTest { get; set; }
}
3. 转换类
//此类用于转换DataTable到List
public class ConvertToList
{
/// <summary>
/// 实际转换的方法--约束泛型参数T只能是应用类型,同时必须包含一个无参构造函数
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public List<T> GetList<T>(DataTable dataTable) where T : class, new()
{
List<T> list = new List<T>();
Type type = typeof(T);
//获取当前的实体的所有公共属性
PropertyInfo[] pis = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach(DataRow row in dataTable.Rows)
{
T t = new T();//创建一个实例
foreach (var item in pis)
{
//如果属性设置了Ingore特性,则直接跳过此属性
Attribute ingore = item.GetCustomAttribute(typeof(IngoreAttribute));
if (ingore != null)
{
continue;
}
string fieldName = item.Name;//取出属性的默认名字当做字段默认名
ColumnNameAttribute columnName = item.GetCustomAttribute(typeof(ColumnNameAttribute)) as ColumnNameAttribute;
//如果当前属性添加了ColumnName特性,则设置字段名为Name
if (columnName != null)
{
fieldName = columnName.Name;
}
//由于演示的关系此处只添加了int类型的转换
if (item.PropertyType == typeof(int))
{
item.SetValue(t, Convert.ToInt16(row[fieldName].ToString()));//为当前属性赋值*注意此处可能存在装箱拆箱的问题
}
else
{
item.SetValue(t, row[fieldName]);
}
}
list.Add(t);
}
return list;
}
}
4. 测试代码
//ConvertToList测试
//此处模拟从数据库获取的数据,实际项目中请从真实的数据库获取数据
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Columns.Add("TelePhone");
dt.Columns.Add("Email");
dt.Columns.Add("IngoreTest");//此字段为忽略字段,列添加此字段只是为了演示转换过程中确实会忽略该字段
DataRow dr1 = dt.NewRow();
dr1["ID"] = 1;
dr1["Name"] = "张三";
dr1["TelePhone"] = "123456";
dr1["Email"] = "test@test.com";
dr1["IngoreTest"] = "Ingore";
dt.Rows.Add(dr1);
DataRow dr2 = dt.NewRow();
dr2["ID"] = 2;
dr2["Name"] = "李四";
dr2["TelePhone"] = "456789";
dr2["Email"] = "qwer@test.com";
dr2["IngoreTest"] = "IngoreTest";
dt.Rows.Add(dr2);
ConvertToList ct = new ConvertToList();
List<CustomEntity> customs = ct.GetList<CustomEntity>(dt);
以上就是通过反射和特性实现转换的功能,更多用法请参考相关教程,谢谢!
C#反射与特性使用简介的更多相关文章
- .NET基础拾遗(4)委托、事件、反射与特性
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...
- 十七、C# 反射、特性和动态编程
反射.特性和动态编程 1.访问元数据 2.成员调用 3.泛型上的反射 4.自定义特性 5.特性构造器 6.具名参数 7.预定义特性 8.动态编程 特性(attribute)是在一个程序集中插入 ...
- 利用反射的特性将DataReader对象转化为List集合
问题:将SqlDataReader对象转换为List<T>集合 思路: 1,利用反射的特性得到对应实体Model的公共属性 Type type = typeof(T); PropertyI ...
- C#图解教程 第二十四章 反射和特性
反射和特性 元数据和反射Type 类获取Type对象什么是特性应用特性预定义的保留的特性 Obsolete(废弃)特性Conditional特性调用者信息特性DebuggerStepThrough 特 ...
- .NET技术-1.0.使用反射、特性简化代码(验证Model类)
使用反射.特性简化代码 参考项目:利用反射验证Model类/AssemblyVerification 假设现在有一个学生类(Student) /// <summary> /// 学生类 / ...
- C#根据反射和特性实现ORM映射实例分析
本文实例讲述了C#根据反射和特性实现ORM 映射的方法.分享给大家供大家参考.具体如下: (一)关于反射 什么是反射? 反射就是在运行时,动态获取对象信息的方法.比如:运行时获得对象有哪些属性,方法, ...
- C#反射与特性(一):反射基础
目录 C#反射与特性(一):反射基础 1. 说明 1.1 关于反射.特性 2. 程序集操作 2.1 获取 程序集对象(Assembly) 2.2 Assembly 使用 2.3 获取程序集的方式 C# ...
- C#反射与特性(五):类型成员操作
目录 1,MemberInfo 1.1 练习-获取类型的成员以及输出信息 1.2 MemberType 枚举 1.3 MemberInfo 获取成员方法并且调用 1.4 获取继承中方法的信息(Decl ...
- C#反射与特性(六):设计一个仿ASP.NETCore依赖注入Web
目录 1,编写依赖注入框架 1.1 路由索引 1.2 依赖实例化 1.3 实例化类型.依赖注入.调用方法 2,编写控制器和参数类型 2.1 编写类型 2.2 实现控制器 3,实现低配山寨 ASP.NE ...
随机推荐
- iis 和 node express 共用80端口 iisnode 全过程
一.首先下载iisnode.exe https://github.com/tjanczuk/iisnode/wiki/iisnode-releases 链接 安装完毕! 二.打开IIS 7 选中 D ...
- lvm快照备份数据库(Mysql5.7)
备份的目的 能够防止由于机械故障以及人为误操作带来的数据丢失,例如将数据库文件保存在了其它地方. 备份的分类 以操作过程中服务的可用性分: 冷备份:cold backup mysql服务关闭,mysq ...
- 2.Git配置和关联GitHub
1.配置本地信息, 右键Git Bush Here git config –global user.name '账号名' ##回车 git config –global user.email 邮箱 # ...
- 【页面置换算法】LRC算法和FIFS算法
算法介绍 FIFO:该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰.该算法实现简单,只需把一个进程已调入内存的页面,按先后次序链接成一个队列,并设置一个指针,称为替换指针, ...
- 删除API
Delete API 删除API允许根据ID从指定索引中删除一个类型化的JSON文档. DELETE /twitter/_doc/1 返回结果如下: { "_index": &qu ...
- Three.js学习笔记04--纹理
1 纹理由图片组成 3D世界的纹理由图片组成. 将纹理以一定的规则映射到几何体上,一般是三角形上,那么这个几何体就有纹理皮肤了. 首先应该有一个纹理类,其次是有一个加载图片的方法,将这张图片和这个纹 ...
- [Swift]LeetCode1011. 在 D 天内送达包裹的能力 | Capacity To Ship Packages Within D Days
A conveyor belt has packages that must be shipped from one port to another within D days. The i-th p ...
- 细说javascripe事件传播流程
当我们使用js时,经常会遇到事件传播流程的问题,下面我说一下我的观点. 在js触发某个事件时会相应生成一个事件对象,而这个事件对象则会根据DOM事件流的方向进传递,而传递的顺序如下图所示: 事件对象会 ...
- SpringBoot 集成Mybatis 连接Mysql数据库
记录SpringBoot 集成Mybatis 连接数据库 防止后面忘记 1.添加Mybatis和Mysql依赖 <dependency> <groupId>org.mybati ...
- C#中为什么会出现空静态构造方法的写法
再过几个小时,就要回家过春节了,今天说些简单点的东西,大家在看C#代码的时候,一定会对这样的写法非常迷茫:在一个类中会出现一个空的静态构造方法.这不是多此一举吗,这样做的目的是什么?今天我就来说说这个 ...