C#温故知新(4)---反射机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wucdsg/article/details/79152958

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);

这里写图片描述

5.引用

猜你喜欢

转载自blog.csdn.net/wucdsg/article/details/79152958