• 概述 什么是反射

Reflection,中文翻译为反射。
        这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:

Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
MethodInfo包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。
诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。

类型 作用
Assembly 通过此类可以加载操纵一个程序集,并获取程序集内部信息
EventInfo 该类保存给定的事件信息
FieldInfo 该类保存给定的字段信息
MethodInfo 该类保存给定的方法信息
MemberInfo 该类是一个基类,它定义了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多个公用行为
Module 该类可以使你能访问多个程序集中的给定模块
ParameterInfo 该类保存给定的参数信息      
PropertyInfo 该类保存给定的属性信息

这些都是废话,我们一起看几个案列就完全学会了,在此说明下,反射用到的一些基础技术有 运行运算符,type 类,这里就不过多的解释了,如有不会可以去园子里面自己去找,本人也写过一篇相关文章,简单的介绍了运行运算符。

  • 如何得到一个类的对象

现有工程文件(项目文件)结构如下

People类代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Entity
{
public class People
{ public People()
{
Console.WriteLine("People被创建了");
}
public People(String Name)
{
this.Name = Name;
Console.WriteLine("People被创建了,并且people的名字是"+this.Name);
}
public string Name { get; set; }//自动属性,在程序实例化的过程中会自动创建私有的字段,这个字段在people 内存中开辟控件存储其值(本文称为公有属性)在此感谢ENC博主的支持和评论,
public int Age { get; set; } public string Sex { get; set; } public string msg;//公有字段 private string qq;//私有字段
private string address;//私有属性
public string Address { get => Address; set => Address = value; }
public override string ToString()
{
return "{" + $"name:{this.Name},age:{this.Age},sex{this.Sex}" + "}";
}
public string Say()
{
return "hello! " + this.Name;
}
}
}

debug 目录如下:

这里说明下,程序中,并没有引用 Entity 类库,也没有引用Entity..DLL文件,请自行引用,我们如果不实例化得到一个对象呢??正常的时候,我们都是通过new 得到一个对象,如:

using Entity;
using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
People p = new People();
Console.WriteLine(p);
People peop = new People("张三");
Console.WriteLine(p);
Console.Read();
}
}
}

我们再来看下类的类型是什么?

using Entity;
using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
Type t = typeof(People);
Console.WriteLine(t);
Type type= Type.GetType("People");
Console.WriteLine(type);//这里是得不到的,因为配件装载只能在程序集内部使用
Console.Read();
}
}
}

我们来学习下,如何根据类类型进行反射。

  • 类的反射

    对象无参构造函数反射

 static void Main(string[] args)
{
Type type = typeof(People);
People people= Activator.CreateInstance(type) as People;//实例化得带一个类
Console.WriteLine(people);
Console.Read();
}

    对象有构造函数参反射

 static void Main(string[] args)
{
Type type = typeof(People);
People people= Activator.CreateInstance(type) as People;//实例化得到一个类
Console.WriteLine(people);
//实例化得到一个类,该类有一个参数
People p = Activator.CreateInstance(type, new object[] { "Wbcsky" }) as People;
Console.WriteLine(p);
Console.Read();
}

    对象泛型反射

 static void Main(string[] args)
{
Type type = typeof(People);
People p1 = Activator.CreateInstance<People>();
Console.WriteLine(p1);
Console.Read();
}

关于对象的反射,就只有这三种形式,分别是泛型反射,泛型反射有且只能得到无参数的实例对象,和普通无参反射像比较,反射反射减少了装箱拆箱的操作。有参数反射我们是按照参数的顺序,传递的object 数组。这些反射都是基于 Activator.CreateInstance 来完成的。

属性字段的反射
  • 获取一个对象的所有属性

   static void Main(string[] args)
{
Type type = typeof(People);
System.Reflection.PropertyInfo[] p = type.GetProperties();
foreach (var item in p)
{
Console.WriteLine("属性名:" + item.Name + "属性类型" + item.PropertyType.FullName + "属性类型命名空间" + item.PropertyType.Namespace);
}
Console.Read();
}

我们都知道,在C#中,属性的封装有两种,一种全写,一种简写,全写的在某些工具书中叫做私有属性,简写的在工具书上叫做公有属性。

如:

   public int Age { get; set; }
我们称为简写,工具书上叫做公有属性。
则:
      private string address;//私有属性
public string Address { get => Address; set => Address = value; }

private string iD;
public string ID

{
get { return this.iD; }
set { this.iD = value; }
}

这种写法我们称为私有属性,私有属性中,当使用=>这种运算的,我们称为lambda表达式写法,使用this 关键字的写法,我们称为面向对象写法。不论哪一种属性,我们都叫做属性,我们在反射中获取属性使用的是Type 类的 .GetProperties()方法来获取类的全部属性。我们来看下执行结果。

这里就不过多的介绍获取属性的值了,我们在下面介绍获取属性的值。
  • 获取指定名称的属性和值及设置一个值

  static void Main(string[] args)
{
Type type = typeof(People);
System.Reflection.PropertyInfo Property = type.GetProperty("Name");//注意属性名称字符串大小写
if (Property == null) Console.Read();//如果属性名称大小写错误或者不存在,我们Property对象将会是null
Console.WriteLine("属性名:" + Property.Name + "属性类型" + Property.PropertyType.FullName + "属性类型命名空间" + Property.PropertyType.Namespace);
//获取属性的值
People p= Activator.CreateInstance(type) as People;//获取对象
object oName = Property.GetValue(p); //获取值
Console.WriteLine("旧" + oName);
Property.SetValue(p, "abc");//设置一个值
oName = Property.GetValue(p); //获取值
Console.WriteLine("新" + oName);
Console.Read();
}

看了上面的代码,我们会发现,获取属性使用的是Type类的 GetProperty方法来完成的。获取值和设置值,使用的是 PropertyInfo  类的 GetValue和Set value 来完成的。执行结果如下

因为初始化的时候是空,所以旧就什么也没有输出。有人会说了,这个没有获取到类,进行点写的方便,为什么要这么写呢,告诉你一句话,存在就是有道理的,这里可以简单的告诉,我们很多时候,一个功能更新过于频繁,我们完全可以把这个类写入配置文件中,去配置这个类对象的功能使用。理解即可,不理解清背下来代码。

  • 获取对象的所以公有字段和私有字段

在这里说明下,很多人都不明白字段和属性的区别,我这里简单说下,理解即可,不理解不影响学习,我们一个类的变量进行封装,会出现get ,set 设置这个字段的访问权限,这个封装我们称为属性,而这个变量我们叫做字段,字段不指定修饰符的时候默认为私有的。

   static void Main(string[] args)
{
Type type = typeof(People);
System.Reflection.FieldInfo[] fi = type.GetFields();
Console.WriteLine("\r\n-------------------- 获取对象的所以公有字段-------------------------------\r\n");
foreach (System.Reflection.FieldInfo item in fi)
{
Console.WriteLine("公有字段名" + item.Name);
}
Console.WriteLine("\r\n-------------------- 获取对象的所有私有字段-------------------------------\r\n");
System.Reflection.FieldInfo[] fiprivate = type.GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic); foreach (System.Reflection.FieldInfo item in fiprivate)
{
Console.WriteLine("私有字段名" + item.Name);
}
Console.Read();
}

这是一个难点,但是在实际开发过程中很少使用,但是这我们必须要会,否则后期写组件开发等文档,该看不懂了,准备好瓜子,咱们开始听故事了。

看了上面的代码,及字段及属性的介绍,我们会发现,输出的结果,共有的很好理解,我们类里面定义的变量 指定public 以后,我们就可以通过

GetFields ()
方法返回我们想要的公有字段数组,我们输出了名字,这里就不过多的解释了。
反射私有字段,输出的这个是什么啊,乱码七招的。

私有字段名<Name>k__BackingField
私有字段名<Age>k__BackingField
私有字段名<Sex>k__BackingField
私有字段名qq
私有字段名address

 其实很好理解,我们在前面说过获取所有属性的时候说过属性分为私有和公有,其中私有属性有两种写法,其实私有属性是对私有变量的封装,也可以说是对私有字段的封装,公有属性是什么呢?
其实公有属性在编译过程中, 为了方便JTL 公共语言运行环境更好的编译,自动生成了一个私有的字段,这个字段是根据操作系统不同生成不同前缀的私有字段,这里生成的是K_前缀的。这样我们就好理解为什么上图会多输出三个字段。
如果此处还不理解,那么请看其他博客吧本文介绍的毕竟都是基础。而实际开发过程中反射这基本使用的都是组件。
  • 获取指定的公有字段

在这里就不介绍获取指定公有字段的值了,和属性获取是一样的。

 static void Main(string[] args)
{
Type type = typeof(People);
Console.WriteLine("\r\n-------------------- 获取对象的指定公有字段-------------------------------\r\n");
Console.WriteLine("字段名" + type.GetField("msg").Name);
Console.Read();
}

代码很简单,只有一行。那么有人会问,那字段分为私有和共有的,为啥没有介绍获取私有属性的呢???为啥没有介绍获取指定私有字段的呢???,其实答案很简单,你看过有封装属性的时候有私有的吗,私有的是不是都说在类的内部使用,那我反射类就可以了,我外部也不使用。那私有字段呢,为啥没有,不是没有,是有但是基本不使用,因为共有属性会默认生成私有字段,这个私有字段的前缀不同,所以无法获取,没意义。所以基本没人使用。

方法和构造函数的反射
  •  获取公有方法并调用

  Type type = typeof(People);
Console.WriteLine("\r\n-------------------- 获取对象的共有方法并且调用-------------------------------\r\n");
System.Reflection.MethodInfo mi = type.GetMethod("Say");
People p= Activator.CreateInstance<People>();
p.Name = "张四伙";//为了省事,这里不使用属性反射添加值了
object oReturn = mi.Invoke(p, null);//第一个参数为反射的对象,第二个参数object 数组,为参数,参数按顺序填写
Console.WriteLine(oReturn);
Console.Read();

这个没有什么解释的了,前面最难的属性字段反射,我们都会了,这个就不是问题了,自己多看看代码?

  • 获取当前类下的所有够着函数

static void Main(string[] args)
{
Type type = typeof(People);
///获取所有的一般不会使用,这里就不过多介绍了
System.Reflection.ConstructorInfo[] info = type.GetConstructors();//获取当前类下所有够着函数
foreach (System.Reflection.ConstructorInfo item in info)
{
Console.WriteLine("是否为虚方法"+item.IsVirtual);
Console.WriteLine("名称"+item.Name);
} Console.WriteLine("\r\n-------------------- 获取当前类下参数类型匹配的够着函数-------------------------------\r\n");
System.Reflection.ConstructorInfo con = type.GetConstructor(new Type[] { typeof(string) });
object o = con.Invoke(new object[] { "zhangsan" });
People peo = o as People;
Console.WriteLine(peo);
Console.Read();
}

大家会说了,够着函数不就是类对象的实例化吗?,我们前面不是讲过反射类对象了吗,为什么这个里面还要获取实例化对象呢?

其实有些时候,我们在使用抽象类和接口的时候,我们通过之前学习的类的反射是一样可以做到得到类的对象,这里之说以这么讲解,因为有一些反射项目在优化的时候,会使用内部查找原则,即从够着函数开始得带类的对象,效率会更高一些。

我们在开发过程中,尽量有内而外,尽量把计算或者声明拿到程序代码执行过程中的最后去做,这样使用内存会少,效率会更高。

下边我们学习这篇文章的第二大核心。程序集反射

程序集反射

什么是程序集反射呢,加入我们三层架构,我不想引用bll层和model 层,也不想引用他们的dll,就能在业务层得带他的对象引用,这个怎么做到呢???我们一起来学习下吧!

首先程序集中删除Entity.dll 程序编译跟目录放置 ectity.dll文件。看下列代码

using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
/*装载程序集*/
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("Entity");
// System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom("Entity.bll");//使用这种方式需要写扩展名
Console.WriteLine("\r\n-------------------- 程序集反射1-------------------------------\r\n");
Type peopleType = assembly.GetType("Entity.People");//得到people 类的type 类型
object obj = Activator.CreateInstance(peopleType);
System.Reflection.MethodInfo me = peopleType.GetMethod("Say");
object ret = me.Invoke(obj, null);
Console.WriteLine(ret);
Console.WriteLine("\r\n-------------------- 程序集反射2-------------------------------\r\n");
object PeopleObj = assembly.CreateInstance("Entity.People");//直接得到类的实例化对象
Console.WriteLine(PeopleObj);
Console.Read();
}
}
}

代码注释已经很明确了,这里就不过多的解释了,我们来看下执行结果 。

-------------------- 程序集反射1-------------------------------

People被创建了
hello!

-------------------- 程序集反射2-------------------------------

People被创建了
{name:,age:0,sex}

在程序集反射中,我们就没有办法在.属性 .字段 .方法的调用了,这个时候,我们只能通过属性,方法的反射去调用了,这里演示的不多,就两种常用的案列,剩下的程序集有参数,无参数够造函数就不多说了,和前面的是一样的,本文只是介绍了开发过程中常用的案列。

泛型反射

现有泛型类如下

  public class GenericClass<T, W, X>
{
public void Show(T t, W w, X x)
{
Console.WriteLine("t.type={0},w.type={1},x.type={2}", t.GetType().Name, w.GetType().Name, x.GetType().Name);
}
}

反射代码如下:

 Assembly assembly = Assembly.Load("Entity");
Type genericType = assembly.GetType("Entity.GenericClass`3");
Type typeNew = genericType.MakeGenericType(typeof(int), typeof(int), typeof(int));
Dynamic dGeneric = Activator.CreateInstance(typeNew);

泛型反射,我们有几个泛型参数,我们就在后边补位“`3”,注意符号 ` 可千万别少了,我们泛型反射是使用Type 类的 MakeGenericType()方法进行获取泛型的Type 类型的,通过这个类型进行反射


总结及扩展

1.反射一般是用在序列化无法完成的情况下,比如接口返回想xml,而这个xml 经常变动,并没有一个指定的规律,这个时候我们就不能用linq to xml 等反序列化对象了。这个时候就应当使用反射了。

2.真正开发过程中,反射不是是向上面这么写的,真正的反射是使用组件来完成的,一般也不会使用程序集反射,除非这个框架的某个功能模块更新频繁,我们可以使用不同的反射区完成,只需要在xml 文件中配置下就可以了。

3.在这里简单介绍下组件反射,不是说开发过程中不会有程序集等反射,而是大多数的情况下组件反射就已经能满足我们的需求了,如AutoFac组件,等其他的。

4.反射技术点一般对应的技术点有 IOC 翻转,依赖倒置,依赖注入等

下边分享一篇文章,之所以写本文,就是因为下边这篇文文章介绍的太主流,很多人不会使用,Autofac是net core 2.0里面的组件,请看下边的文章

文章链接1 Autofac 解释第一个例子 《第一篇》

文章链接2 Autofac 组件、服务、自动装配 《第二篇》

文章链接3 通过配置的方式Autofac 《第三篇》

以上三篇合起来,我们称为IOC 设计模式

 

net 反射30分钟速成的更多相关文章

  1. 【.Net】net 反射15分钟速成

    概述 什么是反射 Reflection,中文翻译为反射.        这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’.‘模块(Module)’. ...

  2. LaTeX新人教程,30分钟从完全陌生到基本入门

    by Nan 对于真心渴望迅速上手LaTeX的人,前言部分可以跳过不看. 本教程面向对LaTeX完全无认知无基础的新人.旨在让新人能够用最简单快捷的方式,轻松入门,能够迅速使用LaTeX完成基本的文本 ...

  3. LaTeX新人30分钟从完全陌生到基本入门

    From:http://www.360doc.com/content/13/0117/11/2886802_260681908.shtml 对于真心渴望迅速上手LaTeX的人,前言部分可以跳过不看. ...

  4. 学习笔记之X分钟速成Python3

    X分钟速成Python3 https://mp.weixin.qq.com/s/QT5sR0nUKgJYsYgrj2SleA https://learnxinyminutes.com/docs/zh- ...

  5. LaTeX新人教程,30分钟从完全陌生到基本入门[转载]

    LaTeX新人教程,30分钟从完全陌生到基本入门[转载] 2017-02-05 分类:TeX讲义 阅读(32514) 评论(0)  这是一篇老文了,前几天看微博的时候看到的,文中的很多表达比较过激,思 ...

  6. 30分钟学会XAML

    1.狂妄的WPF 相对传统的Windows图形编程,需要做很多复杂的工作,引用许多不同的API.例如:WinForm(带控件表单).GDI+(2D图形).DirectX API(3D图形)以及流媒体和 ...

  7. Shell脚本编程30分钟入门

    Shell脚本编程30分钟入门 转载地址: Shell脚本编程30分钟入门 什么是Shell脚本 示例 看个例子吧: #!/bin/sh cd ~ mkdir shell_tut cd shell_t ...

  8. JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)

    前言:关于Vue框架,好几个月之前就听说过,了解一项新技术之后,总是处于观望状态,一直在犹豫要不要系统学习下.正好最近有点空,就去官网了解了下,看上去还不错的一个组件,就抽空研究了下.最近园子里vue ...

  9. 2016windows(10) wamp 最简单30分钟thrift入门使用讲解,实现php作为服务器和客户端的hello world

    2016最简单windows(10) wamp 30分钟thrift入门使用讲解,实现php作为服务器和客户端的hello world thrift是什么 最简单解释 thrift是用来帮助各个编程语 ...

随机推荐

  1. centos部署yapi爬坑记

    前言 这几天终于完成了为期三个月的公司某个demo版的项目,在这期间和公司的后台因为API的事怼过无数次了,'我的接口没问题,是你请求的方式不对吧!'.'一定是你请求的参数不对'......诸如此类问 ...

  2. 初识node.js(通过npm下载项目依赖的包的过程)

    一.初识node.js 简单的说Node.js 就是运行在服务器端的JavaScript. Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台. Node.js是一个事 ...

  3. 一步步Cobol 400上手自学入门教程05 - 表

    在COBOL中有几类典型结构的表.这几类典型结构的表在大体上可分为下标表和索引表两大类.另外,根据表的重复次数定义又有定长表和变长表.此外,表还允许嵌套,因此还有嵌套表.这几类表均符合表的基本定义,都 ...

  4. KahaDB简介

    ActiveMQ 5.3以后,出现了KahaDB.她是一个基于文件支持事务的消息存储器,是一个可靠,高性能,可扩展的消息存储器.     她的设计初衷就是使用简单并尽可能的快.KahaDB的索引使用一 ...

  5. 02-05:springboot文件的上传

    1.在static 下建立upload.html文件 <!DOCTYPE html> <html> <head> <meta charset="UT ...

  6. Vue图片懒加载插件 - vue lazyload的简单使用

    Vue module for lazyloading images in your applications. Some of goals of this project worth noting i ...

  7. PLSQL Developer概念学习系列之登录连接Oracle时出现(没有登录) -PL / SQL Developer:ORA - 12541: TNS :无建听程序的错误解决办法(图文详解)

    不多说,直接上干货! 前期博客 PLSQL Developer概念学习系列之如何正确登录连接上Oracle(图文详解)   如用scott.scott_password进行登录,orcl是全局数据库 ...

  8. 腾讯云点播视频存储(Web端视频上传)

    官方文档 前言 所谓视频上传,是指开发者或其用户将视频文件上传到点播的视频存储中,以便进行视频处理.分发等. 一.简介 腾讯云点播支持如下几种视频上传方式: 控制台上传:在点播控制台上进行操作,将本地 ...

  9. 基于boot2docker部署Docker环境

    Docker轻量级的特性使得我们可以快速打包开发环境:一处编译,到处使用.我们可以在第一次编译好需要的开发环境,然后把镜像导出打包,只有有docker环境,便可以快速还原原来的开发环境. 很常用的一个 ...

  10. Java总结:字符串详解

    更新时间:2018-1-6 21:20:39 更多请查看在线文集:http://android.52fhy.com/java/index.html String 字符串创建 String str1=& ...