为何要把反射和泛型放在一块儿讲呢,这里是处于我的对C#的一个很棒的观感,由于C#的反射是能够获取泛型里的元素的,而不像Java一个让我比较难受的地方就是Java的泛型实际编译的时候会擦除类型信息。
那么问题来了,什么是泛型,什么又是反射呢?程序员
请原谅我先介绍泛型,由于没有泛型基础直接介绍反射是不完整的,就好比说你辛辛苦苦拿到一个类的反射信息,等用的时候才发现结果这是一个泛型类,那还得解析这个类的泛型的信息,这时候就必须先有一个泛型的基础。
那么什么是泛型呢,先看看百度百科给的定义:c#
泛型是程序设计语言的一种特性。容许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须做出指明。各类程序设计语言和其编译器、运行环境对泛型的支持均不同。将类型参数化以达到代码复用提升软件开发工做效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。数组
额,说实话哈,有一部分我没看懂他写的是啥。根据个人理解,泛型就是模板类里套的参数。就比如咱们从网上找到一个好看的PPT模板,咱们在写PPT的时候根据咱们的主题套用这个模板,而后写出一个很好看的PPT,被老板表扬升职加薪。嗯,事实上用好了泛型也会升职加薪。框架
泛型说的笼统一些就是类型参数化的过程,咱们以前介绍的List就是一个泛型类。泛型分泛型类/接口和泛型方法。泛型类和泛型接口能够看作是一种,由于它的泛型参数是用在整个结构体里面的(注意不是结构,struct);泛型方法又有参数泛型和返回值泛型两种。函数
public class Template<T> { private T data; public void SetTemplate(T temp) { data = temp; } public T GetTemplate() { return data; } }
上述示例是一个简单的泛型类,体现了泛型类的特色。在声明类的时候,声明一个泛型占位符T
,在下面的属性、字段、方法的参数和方法的返回值均可以使用这个占位符,约定类型一致。设计
泛型的接口和泛型类是一致的,只不过接口没有方法的实现内容也就是方法体而已。code
// 继续上面的代码 Template<int> temp = new Template<int>(); temp.SetTemplate(10); int ten = temp.GetTemplate();
使用泛型类和普通类不一样的地方就是,泛型类告诉编译器你要传递的类型。使用<> 作标记,中间写类型,表示这是一个泛型为XXX的泛型类。一般与其余语言不一样的地方是,C#的泛型支持全部类型,意思就是在没有额外声明的时候,可使用任意类型做为泛型参数传递。对象
C#也能够声明一个方法为泛型方法,方法的泛型声明是声明在方法名的后面,参数列表的前方。blog
public void TemplateMethod<T>(T arg); public T TemplateMethod1<T>(); public T TemplateMethod2<T>(T arg);
上述三个都是合规的泛型方法声明。泛型能够是参数,也能够是返回值,还能既是返回值又是参数。接口
那么问题来了,多个泛型参数该怎么声明?
以下:
public T2 TemplateMothod3<T1,T2>(T1 arg); public T3 TemplateMothod4<T1,T2,T3>(T1 arg,T2 arg2);
在两个尖括号中间放入多个泛型,而后用逗号隔开,与参数列表和返回值的类型一一对应。
TemplateMethod(10);// 方式 1 int it = TemplateMethod1<int>();// 方式 2
因为篇幅和时间的关系(主要是我写这篇的时候时间有点晚了。。)就不对以前全部的方法进行演示了。
这里简单介绍一下泛型方法的使用:
TemplateMethod<int>(10);
。在实际开发过程当中,咱们会对一些泛型类的泛型参数进行类型约束,那么泛型约束应该怎么写呢,看示例:
public void Demo<T>(T arg) where T : 约束内容 public void Demo<T,P>(T arg,P arg1) where T: 约束内容 where P:约束内容
若是对多个参数进行约束,就写多个where。
泛型的约束有一下几种:
在C#里有个颇有意思的地方,那就是泛型标记。
泛型支持 in/out做为占位符T的前置标记。那这两个标记是什么意义呢,in表示这个类型参数只能做为参数列表的类型进行传递,out表示这是一个返回值的类型,示例以下:
public T2 Demo<in T1,out T2>(T1 t1);
类和方法的标记大同小易,基本上是一致的。
反射在不少地方都有着使用,这里先简单的介绍一下C#中的反射相关内容,由于细讲的话会涉及到不少东西并且还须要不少前置概念,不过在本身写框架以前不须要涉及到太多反射的内容。
反射,英文名 reflect,简单的介绍就是将类型对象化,而后操做这个对象的技术。
咱们先建立一个示例类:
public class Person { public string Name { get; set; } public int Age { get; set; } public Person() { Name = "小李"; Age = 24; } public Person(string name, int age) { Name = name; Age = age; } public string SayHi() { return "你好,我叫" + Name + "个人年纪是 " + Age; } }
首先须要注意的一个类:Type
,这个类是反射技术里的基石,甚至能够说是核心,表示一个类的类型信息。
那么,咱们该如何获取类型对象呢?在C#中常见的有以下两个方法:
typeof
关键字Type personType = typeof(Person);
GetType
方法Person person = new Person(); Type personType = person.GetType();
若是咱们在编写程序的时候,知道要获取什么类的Type对象的话,建议使用typeof获取。若是咱们只有一个对象,须要经过这个对象进行操做的话,那么最好使用GetType来获取。
如今咱们获取到了一个Person类的Type对象,能够用来作什么呢?
personType.Name
personType.GetProperties()
personType.GetMethods()
personType.GetConstructors()
如今咱们一一介绍一下这四种写法:
第一条:顾名思义,获取到的结果是Person
这个值。
第二条:该方法会返回一个类型为PropertyInfo[]
的数组,这个数组里包含着全部使用public
声明的属性。固然也能够经过指定的属性名获取属性对象:personType.GetProperty("Name")
这里会获取到Person类的Name属性。
第三条: 获取该类全部public
的方法,并将其封装成一组类型是MethodInfo
的对象数组。同理,也能够根据方法名进行检索:personType.GetMethod("SayHi")
,就能获取对应的SayHi方法。不过,若是有同名方法的话,就可能会出现获取到的方法不是你想要的了。嗯,这部分会放到精讲反射的时候再来细说。
第四条: 获取构造函数,返回的是一个类型是ConstructorInfo
的数组,表示全部的构造方法,不过惋惜的是,没有根据名字检索的方法了,由于构造方法就一个名。
咱们经过上一小节获取到了一个类的属性PropertyInfo
,如今能够利用这个属性进行后续的操做:
Person person = new Person(); Type personType = person.GetType(); PropertyInfo prop = personType.GetProperty("Name");//获取Name属性 Object value = prop.GetValue(person);// 获取 对象 person 的Name属性值 prop.SetValue(person, "wangyipeng");// 为对象 person的Name属性设置值为 wangyipeng
须要注意的是:
若是 类的属性只有get,那么在调用SetValue时会报错。可能要问了,咱们知道是有set,可是程序怎么判断呢?经过prop.CanWrite
的值进行判断,若是值是true
则代表这个属性能够写入值,不然不能。
同理,能够很轻易的联想到若是只有set,那么GetValue也会报错,与之相对应的就是prop.CanRead属性了。
首先,得到到一个对象里的某一个方法:
Person person = new Person(); Type personType = person.GetType(); MethodInfo method = personType.GetMethod("SayHi");
如今获取到了 方法对象,该怎么执行呢?
MethodInfo有一个Invoke方法,这个方法有两个重载版本。其中有一个是:Invoke(object obj, object[] parameters)
,第一个参数是要执行的方法所属的对象,后面的数组参数是对应方法的参数列表,若是为空则填null便可。该方法有个返回值,类型是object,若是方法是没有返回值的方法,那么Invoke的返回值就是null。
经过反射获取一个类的类型对象有几种方式,先介绍一个不用类型的方式:
Person p = Activator.CreateInstance<Person>();
这种方式有一个要求,Person必须有一个无参的构造函数。
第二种方式:
Type personType = typeof(Person); object p = Activator.CreateInstance(personType);//使用无参构造函数 p = Activator.CreateInstance(personType, "小王", 19);//使用Person(string,int)这个构造函数
当须要传递参数的时候,参数类型必须与对应的构造函数一一对应,若是顺序变了,可能会出现找不到对应类的问题。
第三种:
//types 是参数列表的参数类型集合,顺序与实际参数顺序一致 ConstructorInfo cons = personType.GetConstructor(Type[] types); /* 实际上应该是这个调用方 ConstructorInfo cos = personType.GetConstructor(new[]{ typeof(string), typeof(int)}); */ object person = cos.Invoke(new object[] {"王先生", 19});
这时候一个简单的反射介绍就到这里了,反射这里还有一大篇的内容要将。这部分我会放到基础篇完结以后再作一个统一介绍的。不过先道个歉,没介绍泛型在反射的应用。
注:代码里映射的王先生是我一个故人,最近与他有一些纠纷。
更多内容烦请关注个人博客