• 概述 什么是反射

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. Notes for Apue —— chapter 4 Files and Directories(文件和目录)

    4.1 Introduction 4.2 stat, fstat, fstatat, and lstat Functions The lstat function is similar to stat ...

  2. android 获取 imei号码

    Imei = ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)) .getDeviceId(); 1.加入权限 在manifest.xml ...

  3. 用Socket来简单实现IIS服务器

    刚刚接触ASP.NET编程,为了更好的屡清楚服务器的处理过程,就用Socket模拟服务器来处理请求.用Socket来模拟服务器的时候,同样是自己来封装一些对应的类文件.包括 HttpRequest.H ...

  4. mybatis 控制台打印执行的SQL语句

    1. Spring boot 集成 mybatis [转载]SpringBoot中Mybatis打印sql 1.1 如果使用的是application.properties文件,加入如下配置: log ...

  5. [原创]K8正方系统密码解密工具

    工具: K8_zfsoftDecode编译: 自己查壳组织: K8搞基大队[K8team]作者: K8拉登哥哥博客: http://qqhack8.blog.163.com发布: 2015/8/1 1 ...

  6. 【liferay】5、使用PortletURL进行跨portlet通信 liferay6.2

    [问题] 1.当我们一个页面存在多一个portlet的时候,如在不同的portlet之间传参? [解决办法] 1.在liferay官方有几种方式,比较复杂麻烦,不是太实用,这里不再赘述. 2.通过fr ...

  7. 课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 3、Python Basics with numpy (optional)

    Python Basics with numpy (optional)Welcome to your first (Optional) programming exercise of the deep ...

  8. (转)Python的web服务器

    1.浏览器请求动态页面过程 2.WSGI Python Web Server Gateway Interface (或简称 WSGI,读作“wizgy”). WSGI允许开发者将选择web框架和web ...

  9. 使用makefile

    最近在学习一个处理二维相场问题的c++程序,遇到了makefile文件,之前没有接触过,这里做一个简单的整理. 什么是makefile? 大多程序员使用的windows操作系统,IED都完成了make ...

  10. Java类加载顺序

    很长时间没看这方面的内容了,写篇文章让自己牢记一下,顺便分享一下. 首先,写代码以便检验结果.测试代码: public class Test { public static void main(Str ...