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

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.引用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值