zoukankan      html  css  js  c++  java
  • 谈谈对C#中反射的一些理解和认识(上)

      今天就平常用到的非常多的反射这个技术来做一个总结,当然关于反射需要讲解的东西实在是太多的内容,在一片文章中想要讲解清楚是非常难的,本篇博客也是就自己本人对这些内容学习后的一个总结,当然包括看书和自己写过的一些代码中抽取的一些示例,而且本文也仅限于此时对于这个知识点的理解,希望通过以后的逐步学习能够不断加深对这个知识点的理解。

      首先来看看对于反射的基础知识点。

           1 定义:首先看看MSDN怎样对它进行解释吧

      反射提供了封装程序集、模块和类型的对象(Type 类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。

      2 反射有什么作用?

             A、将类型绑定到现有对象,或从现有对象中获取类型信息,这些信息包括(Assembly   MemberInfo  EventInfo  FieldInfo  MethodBase  ConstructorInfo  MethodInfo  PropertyInfo 等等 )另外可以使用反射动态地创建类型的实例,

            例如:
          1、System.Activator 的CreateInstance方法。该方法返回新对象的引用。
          2、System.Activator 的CreateInstanceFrom 与上一个方法类似,不过需要指定类型及其程序集
          3、System.Appdomain 的方法:CreateInstance,CreateInstanceAndUnwrap,CreateInstranceFrom和CreateInstraceFromAndUnwrap
          4、System.Type的InvokeMember 实例方法:这个方法返回一个与传入参数相符的构造函数,并构造该类型。
          5、System.Reflection.Constructinfo 的Invoke实例方法

         下面通过一段代码来了解这5种创建实例的方法

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.Diagnostics;
    using OptimizeReflection;
    using System.Collections;
    using System.Web;
    
    namespace TestOptimizeReflection
    {
    	public class OrderInfo
    	{
    		public int OrderID { get; set; }
    		public DateTime OrderDate { get; set; }
    		public decimal SumMoney { get; set; }
    		public string Comment { get; set; }
    		public bool Finished { get; set; }
    
    		public int Add(int a, int b)
    		{
    			return a + b;
    		}		
    	}
    
    	class Program
    	{
    		static void Main()
    		{
                TestNewInstance();
    		}
    
            static void TestNewInstance()
            {
                //常规方法创建对象
                OrderInfo testObj = new OrderInfo();
    
                #region 常规反射创建
                //利用反射来动态创建对象
                Type instanceType = typeof(OrderInfo);
                OrderInfo orderInfo = (OrderInfo)Activator.CreateInstance(instanceType);
                #endregion
    
                #region Activator.CreateInstanceFrom 创建
                //注意Assembly.GetEntryAssembly().CodeBase表示当前执行的exe所在的路径,instanceType.FullName表示当前OrderInfo的类型名称TestOptimizeReflection.OrderInfo
                System.Runtime.Remoting.ObjectHandle oh= Activator.CreateInstanceFrom(Assembly.GetEntryAssembly().CodeBase, instanceType.FullName);
                //返回被包装的对象
                OrderInfo orderInfoEx = (OrderInfo)oh.Unwrap();
                #endregion
    
                #region System.AppDomain.CurrentDomain实例创建对象
                System.Runtime.Remoting.ObjectHandle ohEx = System.AppDomain.CurrentDomain.CreateInstance(Assembly.GetEntryAssembly().FullName, instanceType.FullName);
                OrderInfo orderInfoExEx = (OrderInfo)ohEx.Unwrap();
                //合并上面的两步
                OrderInfo orderInfoExExEx = (OrderInfo)System.AppDomain.CurrentDomain.CreateInstanceAndUnwrap(Assembly.GetEntryAssembly().FullName, instanceType.FullName);
                #endregion
    
                #region InvokeMember方法创建实例
                OrderInfo invokeMemberOrderInfo = (OrderInfo)instanceType.InvokeMember(null, BindingFlags.Public| BindingFlags.Instance | BindingFlags.Static|BindingFlags.CreateInstance, System.Type.DefaultBinder, null, null);
                #endregion
    
                #region 调用构造器创建
                //调用无参数的默认构造函数
                ConstructorInfo ci = instanceType.GetConstructor(new Type[] { });
                OrderInfo ciOrderInfo = (OrderInfo)ci.Invoke(null);
                #endregion
            }
    	}
    }  

        B、应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射。

       这个该怎样去理解呢?这个可以用插件系统中的同类思想去解释,在构建插件系统的时候,我们有时候需要主程序去动态地调用插件,可能应用程序只有在执行某一操作的时候才能够去调用相关的DLL,这个时候我们就可以通过反射这种方式来动态调用dll中分特定方法或者类型,这个也是经常使用到的。
        C、反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能。

        D、反射能够调用一些私有方法和字段等。

            这个需要重点去讲述,我们知道常规的实例方法由于受到作用域的影响,很多时候当方法设置为Private或者是其它的限制访问的关键字时就无能为力了,这个也是完全能够体现类的封装性,但是通过反射就能够完全绕开这些限制,下面举出一些对私有变量或字段、方法的一些访问方式,能够直接进行访问,这样还是非常方便的,具体请参考下面的代码。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Dvap.Infrastructure.Utils
    {
        public static class ReflectionUtil
        {
            /// <summary>
            /// 得到私有字段的值
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="instance"></param>
            /// <param name="fieldname"></param>
            /// <returns></returns>
            public static T GetPrivateField<T>(this object instance, string fieldname)
            {
                BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
                Type type = instance.GetType();
                FieldInfo field = type.GetField(fieldname, flag);
                return (T)field.GetValue(instance);
            }
    
            /// <summary>
            /// 设置私有成员的值
            /// </summary>
            /// <param name="instance"></param>
            /// <param name="fieldname"></param>
            /// <param name="value"></param>
            public static void SetPrivateField(this object instance, string fieldname, object value)
            {
                BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
                Type type = instance.GetType();
                FieldInfo field = type.GetField(fieldname, flag);
                field.SetValue(instance, value);
            }
    
            /// <summary>
            /// 得到私有属性的值
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="instance"></param>
            /// <param name="propertyname"></param>
            /// <returns></returns>
            public static T GetPrivateProperty<T>(this object instance, string propertyname)
            {
                BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
                Type type = instance.GetType();
                PropertyInfo field = type.GetProperty(propertyname, flag);
                return (T)field.GetValue(instance, null);
            }      
    
            /// <summary>
            /// 设置私有属性的值
            /// </summary>
            /// <param name="instance"></param>
            /// <param name="propertyname"></param>
            /// <param name="value"></param>
            public static void SetPrivateProperty(this object instance, string propertyname, object value)
            {
                BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
                Type type = instance.GetType();
                PropertyInfo field = type.GetProperty(propertyname, flag);
                field.SetValue(instance, value, null);
            }
    
            /// <summary>
            /// 调用私有方法
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="instance"></param>
            /// <param name="name"></param>
            /// <param name="param"></param>
            /// <returns></returns>
            public static T CallPrivateMethod<T>(this object instance, string name, params object[] param)
            {
                BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic;
                Type type = instance.GetType();
                MethodInfo method = type.GetMethod(name, flag);
                return (T)method.Invoke(instance, param);
            }
        }
    }
    

        E 还可以通过反射来获取属性或者类上的特性

        有时候我们需要在类或者属性上面添加自定义的特性,并且通过反射能够获取到这些特性,那么这个时候我们就可以使用反射来达到目的了。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.Diagnostics;
    using OptimizeReflection;
    using System.Collections;
    using System.Web;
    
    namespace TestOptimizeReflection
    {
    	
        /// <summary>
        /// 自定义特性 属性或者类可用  支持继承
        /// </summary>
        [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, Inherited = true)]
        public class EnitityMappingAttribute : Attribute
        {
            private string tableName;
            /// <summary>
            /// 实体实际对应的表名
            /// </summary>
            public string TableName
            {
                get { return tableName; }
                set { tableName = value; }
            }
    
            private string columnName;
            /// <summary>
            /// 中文列名
            /// </summary>
            public string ColumnName
            {
                get { return columnName; }
                set { columnName = value; }
            }
        }
    
        /// <summary>
        /// 会员 ,实际的表名叫MemberInfo,并不是和实体名一致
        /// </summary>
        [EnitityMapping(TableName = "MemberInfo")]
        public class Member
        {
            private int id;
            [EnitityMapping(ColumnName = "关键字")]
            public int Id
            {
                get { return id; }
                set { id = value; }
            }
    
            private string userName;
            [EnitityMapping(ColumnName = "会员注册名")]
            public string UserName
            {
                get { return userName; }
                set { userName = value; }
            }
    
            private string realName;
            [EnitityMapping(ColumnName = "会员真实名")]
            public string RealName
            {
                get { return realName; }
                set { realName = value; }
            }
    
            private bool isActive;
            /// <summary>
            /// 是否活跃  没有附加自定义属性
            /// </summary>
            public bool IsActive
            {
                get { return isActive; }
                set { isActive = value; }
            }
        }    
    
        class Program
    	{
            /// <summary>
            /// 通过反射取自定义属性
            /// </summary>
            /// <typeparam name="T"></typeparam>
            private static void DisplaySelfAttribute<T>() where T : class, new()
            {
                string tableName = string.Empty;
                List<string> listColumnName = new List<string>();
                Type objType = typeof(T);
                //取属性上的自定义特性
                foreach (PropertyInfo propInfo in objType.GetProperties())
                {
                    object[] objAttrs = propInfo.GetCustomAttributes(typeof(EnitityMappingAttribute), true);
                    if (objAttrs.Length > 0)
                    {
                        EnitityMappingAttribute attr = objAttrs[0] as EnitityMappingAttribute;
                        if (attr != null)
                        {
                            listColumnName.Add(attr.ColumnName); //列名
                        }
                    }
                }
    
                //取类上的自定义特性
                object[] objs = objType.GetCustomAttributes(typeof(EnitityMappingAttribute), true);
                foreach (object obj in objs)
                {
                    EnitityMappingAttribute attr = obj as EnitityMappingAttribute;
                    if (attr != null)
                    {
                        tableName = attr.TableName;//表名只有获取一次
                        break;
                    }
                }
                if (string.IsNullOrEmpty(tableName))
                {
                    tableName = objType.Name;
                }
                Console.WriteLine(string.Format("The TableName of the entity is:{0} ", tableName));
                if (listColumnName.Count > 0)
                {
                    Console.WriteLine("The Columns of the table are as follows:");
                    foreach (string item in listColumnName)
                    {
                        Console.WriteLine(item);
                    }
                }
            }
    
            static void Main()
    		{
                DisplaySelfAttribute<Member>(); //显示结果
                Console.ReadLine();
    		}
    	}
    }
    

      最终呈现的效果:

      这里简单说一下AttributeUsage的一些用法,AttributeUsage 有三个 属性 ,分别是 

               public bool AllowMultiple { get; set; }   作用:是否能在一个目标身上多次使用

               public bool Inherited { get; set; } 作用 :特性是否能继承到子类身上

               public AttributeTargets ValidOn { get; } 作用:设置特性的可使用范围

      这里面就是要解释一下AttributeTargets 的特性使用范围,这个主要是用在继承自Attribute的子类里面,在本例中我们自定义的特性主要用于标识类和属性

    public enum AttributeTargets  
       {  
           // 摘要:  
           //     可以对程序集应用特性。  
           Assembly = 1,  
           //  
           // 摘要:  
           //     可以对模块应用特性。  
           Module = 2,  
           //  
           // 摘要:  
           //     可以对类应用特性。  
           Class = 4,  
           //  
           // 摘要:  
           //     可以对结构应用特性,即值类型。  
           Struct = 8,  
           //  
           // 摘要:  
           //     可以对枚举应用特性。  
           Enum = 16,  
           //  
           // 摘要:  
           //     可以对构造函数应用特性。  
           Constructor = 32,  
           //  
           // 摘要:  
           //     可以对方法应用特性。  
           Method = 64,  
           //  
           // 摘要:  
           //     可以对属性应用特性。  
           Property = 128,  
           //  
           // 摘要:  
           //     可以对字段应用特性。  
           Field = 256,  
           //  
           // 摘要:  
           //     可以对事件应用特性。  
           Event = 512,  
           //  
           // 摘要:  
           //     可以对接口应用特性。  
           Interface = 1024,  
           //  
           // 摘要:  
           //     可以对参数应用特性。  
           Parameter = 2048,  
           //  
           // 摘要:  
           //     可以对委托应用特性。  
           Delegate = 4096,  
           //  
           // 摘要:  
           //     可以对返回值应用特性。  
           ReturnValue = 8192,  
           //  
           // 摘要:  
           //     可以对泛型参数应用特性。  
           GenericParameter = 16384,  
           //  
           // 摘要:  
           //     可以对任何应用程序元素应用特性。  
           All = 32767,  
       }  
    

      

  • 相关阅读:
    C# API 大全
    也说_T、_TEXT、TEXT、L
    项脊轩志--归有光
    C# tostring()汇总
    StructLayout特性
    关于C#静态构造函数的几点说明
    C#生成DLL文件
    做.net的早晚会用到,并且网上还没有这方面的正确资料或几乎很少
    C# 实现屏幕键盘 (ScreenKeyboard)
    Microsoft .NET Native
  • 原文地址:https://www.cnblogs.com/seekdream/p/8477329.html
Copyright © 2011-2022 走看看