1.引言
第一次接触反射是在一个插件框架中开发功能,由于是当时还是一枚菜鸟(现在也是o( ̄︶ ̄)o),并没有去想整个框架是怎么运行的,代码写在哪里,然后怎么放,跟着一运行效果就出来,那种插件式即插即拔的效果是怎么运行的并不知道,后来慢慢的尝试去看框架代码,原来是这样(后续会介绍我对插件式开发的理解)。本篇集中于反射的基础知识,不在传道受业,意在方便阅读复习,若篇中有纰漏乃之处,请留言指出,不必留情!
2.忠告
反射的作用是动态的创建类型实例,将类型邦定到现有对象,或从现有对象中获取类型,这种机制在实际应用程序中使用的并不多,除非是特殊需求,一般情况如果框架设计合理是不需要用到反射机制的。再者反射机制里使用动态绑定是需要以牺牲性能为代价的,所以好用但慎用或者说少用,除非避无可避!
3.说明
- 反射之后与实际类的对应关系
反射对象 | 说明 |
---|---|
Assembly | 程序集 |
Type | 类或者本质是类的对象 |
ConstructorInfo | 构造函数 |
FieldInfo | 字段 |
PropertyInfo | 属性 |
MethodInfo | 函数 |
EventInfo | 事件 |
ParameterInfo | 函数的参数 |
Member | 成员(包含字段、属性、方法等等) |
- BindingFlags枚举,查找过滤(不全,深究可以查阅MSDN)
枚举 | 说明 |
---|---|
Default | 不指定绑定标志,等同于null。 |
IgnoreCase | 指定当绑定时不应考虑成员名的大小写。 |
DeclaredOnly | 指定只应考虑在所提供类型的层次结构级别上声明的成员,不考虑继承成员。 |
Instance | 指定静态成员将包括在搜索中。 |
Static | 指定静态成员将包括在搜索中。 |
Public | 指定公共成员将包括在搜索中。 |
NonPublic | 指定非公共成员将包括在搜索中。 |
FlattenHierarchy | 返回层次结构上的公共静态成员和受保护的静态成员。 不返回继承类中的私有静态成员。 静态成员包括字段、方法、事件和属性。 不返回嵌套类型 |
InvokeMethod | 指定要调用一个方法。 它不能是构造函数或类型初始值设定项。 |
CreateInstance | 指定“反射”应该创建指定类型的实例。 调用与给定参数匹配的构造函数。 忽略提供的成员名。 如果未指定查找类型,将应用 (Instance |
GetField | 指定应返回指定字段的值。 |
SetField | 指定应设置指定字段的值。 |
GetProperty | 指定应返回指定属性的值。 |
SetProperty | 指定应设置指定属性的值。 对于 COM 属性,指定此绑定标志与指定 PutDispProperty 和 PutRefDispProperty 是等效的。 |
IgnoreReturn | 在 COM 互操作中用于指定可以忽略成员的返回值。 |
4.示例
本节主要是通过解析一个简单反射的Demo来逐步展示反射的基本操作,待解析的类库就包含两个类:Person和Programmer
/// <summary>
/// Function:Person类
/// Programmer:WUC
/// Data:2018/01/23
/// </summary>
public class Person
{
protected string _name;
protected int _age;
/// <summary>
/// 名称
/// </summary>
public string Name
{
get { return this._name; }
set { this._name = value; }
}
/// <summary>
/// 年龄
/// </summary>
public int Age
{
get { return this._age; }
set { this._age = value; }
}
public Person() { }
public Person(string _name)
{
this._name = _name;
}
public Person(string _name, int _age)
{
this._name = _name;
this._age = _age;
}
public virtual string Argue()
{
return "sb,all your family are sb";
}
public virtual string Love()
{
return "I love you!";
}
}
/// <summary>
/// Function:程序员类
/// Programmer:WUC
/// Date:2018/01/23
/// </summary>
[Description("蛋疼的职业")]
public class Programmer:Person,IComparable<Programmer>,IEquatable<Programmer>
{
protected string _language;
/// <summary>
/// 编程语言
/// </summary>
public string Language
{
get { return this._language;}
set { this._language = value; }
}
public Programmer() { }
public Programmer(string _name) : base(_name) { }
public Programmer(string _name, int _age) : base(_name, _age) { }
public Programmer(string _name, int _age,string _language):base(_name,_age)
{
this._language = _language;
}
public string Coding()
{
return "Hello motherfucker!";
}
public override string Argue()
{
return "This code is not what I wrote,shit!";
}
public override string Love()
{
return "Would you mind make love with me tonight?";
}
private string PrivateFunction(string parameter)
{
return string.Format("parameter:{0},{1}", parameter, "The fucking private function was invoked!");
}
public int CompareTo(Programmer other)
{
if (other == null) return -1;
return this._age - other._age;
}
public bool Equals(Programmer other)
{
if (other == null) return false;
return other._age == this._age && other._language == this._language;
}
public override string ToString()
{
return string.Format("Name:{0},Age:{1},Language:{2}", this._name, this._age, this._language);
}
下面主要是对生成的ReflectionClass.dll程序集进行解析,注意不直接引用程序集
- 加载程序集
string assemblyPath = @"E:\xxx\xxx\xxxxx";
Assembly assembly = Assembly.LoadFile(assemblyPath);
注意Assembly提供了三种加载程序集的方式:Load(),LoadFrom(),LoadFile(),他们的差别这里不做过多区别,如果需要深究,可以参考:C#反射-Assembly.Load、LoadFrom与LoadFile进阶
- 获取Type
//获取所有Type
Type[] types = assembly.GetTypes();
//获取指定的Type(伪代码)
Type type = assembly.GetType("xxx");
//或者常见的还有以下两种方式:
//typeof(类型名称)
Type type_string = typeof(string);
Type type_int = typeof(int)
//实例.GetType()
Type type_string = "wuc".GetType();
Type type_int=2222.GetType();
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
Console.WriteLine(string.Format("******** {0} ********", type.FullName));
Console.WriteLine(string.Format("name:{0}", type.Name));
Console.WriteLine(string.Format("namespace:{0}", type.Namespace));
}
- 指定获取Type
//获取指定的Type(全名称)
Type programmerType = assembly.GetType("ReflectionClass.Programmer");
Type personType = assembly.GetType("ReflectionClass.Person");
- 获取标签(Description)
//看看类上面贴了那些标签
List<Attribute> attributes = programmerType.GetCustomAttributes().ToList();
foreach(Attribute attribute in attributes)
{
Type attributeType = attribute.GetType();
Console.WriteLine("标签:" + attributeType.FullName);
}
DescriptionAttribute des = attributes[0] as DescriptionAttribute;
Console.WriteLine("标签描述:" + des.Description);
- 查看父类及接口实现情况
Console.WriteLine("*********************** 继承 ************************");
//看看类实现了那些接口,以及父类(也可以使用IsAssignableFrom,IsSubclassOf判断)
Console.WriteLine(string.Format("父类:{0}", programmerType.BaseType.FullName));
Type[] interfaceTypes = programmerType.GetInterfaces();
foreach (Type interfaceType in interfaceTypes)
{
Console.WriteLine(string.Format("实现接口:{0}", interfaceType.ToString()));
}
- 获取Programmer类型的字段和属性
Console.WriteLine("*********************** 字段 ************************");
//获取字段(BindingFlags.DeclaredOnly可过滤掉继承的字段)
FieldInfo[] fieldInfos = programmerType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach(FieldInfo fieldInfo in fieldInfos)
{
Console.WriteLine(string.Format("Name:{0},Type:{1}", fieldInfo.Name, fieldInfo.FieldType));
}
Console.WriteLine("*********************** 属性 ************************");
//获取属性
PropertyInfo[] propertyInfos = programmerType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
Console.WriteLine(string.Format("Name:{0},Type:{1}", propertyInfo.Name, propertyInfo.PropertyType));
}
- 获取Programmer类型的构造函数
Console.WriteLine("*********************** 构造函数 ************************");
//获取构造函数
ConstructorInfo[] constructorInfos = programmerType.GetConstructors();
foreach (ConstructorInfo constructorInfo in constructorInfos)
{
Console.WriteLine(string.Format("构造函数名:{0}", constructorInfo.Name));
ParameterInfo[] parameters = constructorInfo.GetParameters();
for (int i = 0; i < parameters.Count(); i++)
{
Console.WriteLine(string.Format("参数{0}:name:{1},type:{2}", i + 1, parameters[i].Name, parameters[i].ParameterType));
}
Console.WriteLine();
}
- 获取Programmer类型的普通方法
Console.WriteLine("*********************** 普通方法 ************************");
MethodInfo[] methodInfos = programmerType.GetMethods();//可以设置BindingFlags过滤
foreach (MethodInfo methodInfo in methodInfos)
{
Console.WriteLine(string.Format("函数名:{0}", methodInfo.Name));
ParameterInfo[] parameters = methodInfo.GetParameters();
for (int i = 0; i < parameters.Count(); i++)
{
Console.WriteLine(string.Format("参数{0}:name:{1},type:{2}", i + 1, parameters[i].Name, parameters[i].ParameterType));
}
Console.WriteLine();
}
以上简单介绍了怎么反射获取特性、构造函数、字段、属性等等,其实获取到之后还可以有一些其他的判断方法,比如说:IsTatic(),IsPublic()等等(有兴趣可以自己钻研哦!)。如果仅仅是看看这些那不是我们的初衷,反射出来,是要动态创建实例的啊,设置字段或者属性值的啊,是要调用函数的啊!看看下面的几点:
- 创建Programmer实例
Console.WriteLine("*********************** 构造实例 ************************");
//方法一:Assembly.CreateInstance("类的完全限定名(即包括命名空间)")(注意参数的传递)
object programmer_assembly = assembly.CreateInstance("ReflectionClass.Programmer", true, BindingFlags.Public | BindingFlags.Instance, null, new object[] { "lj", 26, "C++" }, null, null);
Console.WriteLine("assembly.CreateInstance:{0}", programmer_assembly.ToString());
//方法二:Activator.CreateInstance("类的完全限定名(即包括命名空间)")
object programmer_activator = Activator.CreateInstance(programmerType, "WUC", 21, "C#");
Console.WriteLine("Activator:{0}", programmer_activator.ToString());
//方法三:直接调用构造函数(注意参数的传递)
ConstructorInfo ctor = programmerType.GetConstructor(new Type[] { typeof(string), typeof(int), typeof(string) });
object[] ctor_parameters = new object[] { "wpy", 28, "C#" };
object programmer_ctor = ctor.Invoke(ctor_parameters);
Console.WriteLine("Constructor:{0}", programmer_ctor.ToString());
- 动态设置字段(包括私有)和属性值
//私有
Console.WriteLine("*********************** 设置字段 ************************");
FieldInfo nameFiled = programmerType.GetField("_name", BindingFlags.NonPublic | BindingFlags.Instance);
nameFiled.SetValue(programmer_activator, "test");
FieldInfo ageFiled = programmerType.GetField("_age", BindingFlags.NonPublic | BindingFlags.Instance);
ageFiled.SetValue(programmer_activator, 1000);
FieldInfo languageFiled = programmerType.GetField("_language", BindingFlags.NonPublic | BindingFlags.Instance);
languageFiled.SetValue(programmer_activator, "C++");
Console.WriteLine("字段值设置之后:{0}", programmer_activator.ToString());
Console.WriteLine("*********************** 设置属性 ************************");
Console.WriteLine("属性值设置之前:{0}", programmer_activator.ToString());
//设置属性值(public)
PropertyInfo nameProperty = programmerType.GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
nameProperty.SetValue(programmer_activator, "lj");
PropertyInfo ageProperty = programmerType.GetProperty("Age", BindingFlags.Public | BindingFlags.Instance);
ageProperty.SetValue(programmer_activator, 26);
PropertyInfo languageProperty = programmerType.GetProperty("Language", BindingFlags.Public | BindingFlags.Instance);
languageProperty.SetValue(programmer_activator, "java");
Console.WriteLine("属性值设置之后:{0}", programmer_activator.ToString());
- 调用方法(包含私有方法)
Console.WriteLine("*********************** 调用方法 ************************");
MethodInfo privateFunc = programmerType.GetMethod("PrivateFunction", BindingFlags.NonPublic | BindingFlags.Instance);
string privateFunc_result = privateFunc.Invoke(programmer_activator, new object[] { "param_one" }).ToString();
Console.WriteLine("私有方法:{0}", privateFunc_result);
MethodInfo publicFunc = programmerType.GetMethod("Argue", BindingFlags.Public | BindingFlags.Instance);
string publicFunc_result = publicFunc.Invoke(programmer_activator, null).ToString();
Console.WriteLine("共有方法:{0}", publicFunc_result);