一、基础
C#反射属于相对高级的话题,学会了反射,你可以给你自己的程序增加一些动态的功能。
要点:
1.程序集 Assembly类
load() GetType() CreateInstance()
2.Type类
主要函数 :
- GetConstructor()
-
GetEvent()
-
GetField()
-
GetMethod()
-
GetProperty()
知道了以上函数,就可以应付大多数情况需要反射处理的情况
二、实例1 获取程序集中的类的信息
先写一个需要编译成dll的程序集
namespace Common
{
public class Person
{
public event Action<int> blink;
public event Action<string> blink2;
public Person(int age, string name)
{
this.Age = age;
this.Name = name;
}
public int Age { get; set; }
public string Name { get; set; }
private int Age2 { get; set; }
protected int Age3 { get; set; }
public string Sex;
private string Sex2;
public void SayHello()
{
Console.WriteLine("我是Person类中的函数");
}
private void SayHello2()
{
Console.WriteLine("我是Person类中的函数2");
}
public void Write()
{
File.WriteAllText("Person.txt", "张三李四王五赵六田七");
}
}
}
下面是使用反射
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//反射 Assembly
using System.Reflection;
using System.IO;
namespace HelloReflection
{
class Program
{
static void Main(string[] args)
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ClassLibrary1.dll");
//首先加载程序集文件
Assembly ass = Assembly.LoadFile(path);
Console.WriteLine("加载程序集成功");
#region 获得程序集中数据类型的3种方式
//获得程序集中定义的所有类型,包括公开的和不公开的
Type[] types = ass.GetTypes();
//获得程序集中定义的公共类型
//Type[] types = ass.GetExportedTypes();
foreach (Type t in types)
{
Console.WriteLine(t.Name);
Console.WriteLine(t.FullName);
Console.WriteLine(t.Namespace);
}
//获得程序集中指定的类型
Type type = ass.GetType("Common.Person");
Console.WriteLine(type.Name);
Console.WriteLine("----------");
#endregion
//从程序集中查找指定类型,然后使用系统激活器创建它的实例(实际上调用类型的默认无参构造函数)
//object obj = ass.CreateInstance("Common.Person");
//使用Activator动态创建带有自定义构造函数的类型的实例
object obj = Activator.CreateInstance(type, 18, "张三");
Console.WriteLine(obj.GetType());
//获得数据源中的属性数组
PropertyInfo[] pros = obj.GetType().GetProperties();
foreach (PropertyInfo p in pros)
{
Console.WriteLine(p.Name);
}
Console.WriteLine();
Console.WriteLine("methods");
//获得数据源中的方法
MethodInfo[] mds = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public);
foreach (MethodInfo m in mds)
{
Console.WriteLine(m.Name);
}
Console.WriteLine();
MethodInfo mdi = obj.GetType().GetMethod("SayHello2",BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public);
mdi.Invoke(obj, null);
Console.WriteLine();
//获得数据源中的字段
FieldInfo[] fis = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic |
BindingFlags.Public);
foreach (FieldInfo f in fis)
{
Console.WriteLine(f);
}
Console.WriteLine();
//获得数据源中的构造函数
ConstructorInfo[] cis = obj.GetType().GetConstructors();
foreach (ConstructorInfo c in cis)
{
Console.WriteLine(c);
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("----event------");
EventInfo[] eifs = obj.GetType().GetEvents();
foreach(var e in eifs)
{
Console.WriteLine(e);
}
Console.WriteLine();
Console.ReadKey();
}
}
}
输出结果(部分)
从以上输出 结果可以看到
- 反射 GetMethods() 将属性的get,set也作为方法输出
- 反射GetMethod()通过添加绑定标识BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public可以反射出类的私有函数并调用
- 反射GetFields()通过绑定标识也可以反射出类的私有字段,但是不加标识,默认只反射出公有字段,注意事件也算是字段(field)
- GetEvents()方法就是将类中的event事件列举出来。(关于event关键字,实际在C#编程中,用不用区别几乎没有,如果你注意内在美,可以加上这个词)
三.实例2 利用反射动态创建结构体的实例数组并赋值
需要编译成dll的结构体
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace Common
{
public struct ServiceMsg
{
public int Id;
public long NowTime;
}
}
测试实例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//反射 Assembly需要的命名空间
using System.Reflection;
using System.IO;
namespace ReflectTest
{
class Program
{
static void Main(string[] args)
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ClassLibrary1.dll");
//首先加载程序集文件
Assembly ass = Assembly.LoadFile(path);
Console.WriteLine("加载程序集成功");
//获得程序集中定义的所有类型,包括公开的和不公开的
Type typeAss = ass.GetType("Common.ServiceMsg");
//创建数组
var InstanceArray = Array.CreateInstance(typeAss, 3);
for (int i = 0; i < InstanceArray.Length; i++)
{
//使用get方法获取InstanceArray[i]
//注意invoke参数i是指定数组中的索引值
var serviceMsgItem = InstanceArray.GetType()
.GetMethod("Get", new Type[] { typeof(int) })
.Invoke(InstanceArray, new object[] { i });
FieldInfo fiId = serviceMsgItem.GetType().GetField("Id");
fiId.SetValue(serviceMsgItem, i+100);
FieldInfo fiNowTime = serviceMsgItem.GetType().GetField("NowTime");
fiNowTime.SetValue(serviceMsgItem, DateTime.Now.Ticks);
InstanceArray.GetType()
.GetMethod("Set", new Type[] { typeof(int), serviceMsgItem.GetType() })
.Invoke(InstanceArray, new object[] { i, serviceMsgItem });
}
}
}
}
执行结果(instanceArray被成功赋值):
要点:
- Array.CreateInstance()创建数组
- InstanceArray.GetType().GetMethod("Get", new Type[] { typeof(int) }).Invoke(InstanceArray, new object[] { i }) 获取结构体数组其中一项
- InstanceArray.GetType()
.GetMethod("Set", new Type[] { typeof(int), serviceMsgItem.GetType() })
.Invoke(InstanceArray, new object[] { i, serviceMsgItem }) 将值再设置回去,因为结构体是按值传递的
如果是类数组,注意Array.CreateInstance()创建的数组,每一项的值需要重新动态实例化一个类加入到instanceArray中,这里不再举例。