• C# 反射


    反射概念:

    1. 在程序运行时动态获取类或对象的信息,具体包括了

    动态获取加载程序集(Assmebly)

    动态获取类型信息(如类、接口 等) - Type对象

    动态获取类型的成员信息(如方法,字段,属性等);

    2. 在运行时,动态创建类型实例(new),以及调用和访问这些实例成员;

    .Net反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为.Net的反射机制。

    .Net反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;

    反射的用途
    类型 作用
    Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例。
    Module 包含模块的程序集以及模块中的类等。还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
    ConstructorInfo 构造函数的名称、参数、访问修饰符(如 public 或 private)和实现详细信息(如 abstract 或 virtual)等。使用Type的 GetConstructors 或 GetConstructor 方法来调用特定的构造函数。
    MethodInfo
    方法的名称、返回类型、参数、访问修饰符(如 public 或 private)和实现详细信 息(如 abstract 或 virtual)等。使用Type的 GetMethods 或 GetMethod 方法来调用特定的方法。
    FieldInfo 字段的名称、访问修饰符(如 public 或 private)和实现详细信息(如 static)等;并获取或设置字段值。
    EventInfo 事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等;并添加或移除事件处理程序。
    PropertyInfo 属性的名称、数据类型、声明类型、反射类型和只读或可写状态等;并获取或设置属性值。
    ParameterInfo 参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等

    在程序运行时,动态获取程序集:

    class Program
        {
            static void Main(string[] args)
            {
                Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies();
                Person p = new Person();
                p.TestAssembly();
    
                Console.ReadKey();
            }
        }
    
    
    class Person
        {
            public string Name { get; set; }
            public int Age { get; set; }
            private int Id;
            public double Sum;
    
            public void Method1() { }
            private void Method2() { }
    
            public void TestAssembly()
            {
                Assembly ass = this.GetType().Assembly;
                Console.WriteLine(ass);
                Type[] types = ass.GetTypes();
                foreach (var item in types)
                {
                    Console.WriteLine(item + "   (types)");
                }
                Type currentType = ass.GetType();
                Console.WriteLine(currentType);
                Type typeByFullName = ass.GetType("ConsoleApp2.Person");
                Console.WriteLine(typeByFullName);
    
                Type type = this.GetType();
                Console.WriteLine(type);
                MethodInfo[] methods = this.GetType().GetMethods();
                foreach (var item in methods)
                {
                    Console.WriteLine(item + "    (methods)");
                }
                var members = this.GetType().GetMembers();
                foreach (var item in members)
                {
                    Console.WriteLine(item);
                }
                var member = this.GetType().GetMember("Name");
                foreach (var item in member)
                {
                    Console.WriteLine(item);
                }
                FieldInfo field = type.GetField("Sum");
                Console.WriteLine(field);
                PropertyInfo prop = type.GetProperty("Name");
                Console.WriteLine(prop);
            } 
        }
    View Code

    在程序运行时,动态创建类型实例:

    class Program
        {
            static void Main(string[] args)
            {
                Person p = new Person();
                p.CreatePersonObject();
    
                Console.ReadKey();
            } 
        }
    
    
    
    public void CreatePersonObject()
            {
                Type type = this.GetType();
                Person p = Activator.CreateInstance(type) as Person;
                Person p1 = Activator.CreateInstance<Person>();
    
                PropertyInfo prop = type.GetProperty("Name");
                prop.SetValue(p1, "toto",null);
                Console.WriteLine(p1.Name);
    
                MethodInfo method = type.GetMethod("SayHi");
                method.Invoke(p1, null);
                MethodInfo method1 = type.GetMethod("ShowNumber");
                object[] arrParams = { 13 };
                method1.Invoke(p1, arrParams);
                MethodInfo method2 = type.GetMethod("GetString");
                string retStr = method2.Invoke(p1, null).ToString();
                Console.WriteLine(retStr);
            }
    
            public void SayHi()
            {
                Console.WriteLine("Hiiiiiiiiiiii");
            }
            public void ShowNumber(int no)
            {
                Console.WriteLine(no);
            }
            public string GetString()
            {
                return "Hello";
            }
    View Code

    通过反射找出类型的构造函数,然后调用构造函数来获取实例引用

    public static void GetConstructor()
            {
                Type tpdatetime = Type.GetType("System.DateTime", false);
                if (tpdatetime != null)
                {
                    ConstructorInfo constructor = tpdatetime.GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) });
                    if (constructor != null)
                    {
                        object instance = constructor.Invoke(new object[] { 2019, 12, 17, 16, 11, 24 });
                        if (instance != null)
                        {
                            PropertyInfo[] props = tpdatetime.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                            Console.WriteLine("以下是DateTime实例的属性值列表");
                            foreach (PropertyInfo item in props)
                            {
                                object objval = item.GetValue(instance,null);
                                Console.WriteLine("{0,-15}:{1}",item.Name,objval??string.Empty);
                            }
                        }
                    }
                }
            }
    View Code

    原文链接:https://www.cnblogs.com/czx1/p/201413137070-com.html

    Activator.CreateInstance和Assembly.CreateInstance。这2种方法创建实例的区别:

    https://www.cnblogs.com/loveleaf/p/9923970.html

    Load、LoadFile、LoadForm:

    Assembly assembly = Assembly.Load("Ruanmou.DB.Sqlserver");//1 动态加载 默认加载当前路径的dll文件,不需要后缀,该方法传入的是Dll的名字,该Dll必须位于全局缓存GAC中才行,不然会报“System.IO.FileLoadException: 未能加载文件或程序集”的异常。
    Assembly assembly1 = Assembly.LoadFile(@"E:online720160928Advanced7Course2ReflectionMyReflectionMyReflectioninDebugRuanmou.DB.Sqlserver.dll");// 必须是完整路径
    Assembly assembly2 = Assembly.LoadFrom("Ruanmou.DB.Sqlserver.dll");// 可以是当前路径  也可以是完整路径

    动态判断对象属性值

    判断一个对象里面 所有属性的值是否都为true:

    public class Temp
    {
        public bool CalculationCompleted { get; set; }
        public bool CollectionCompleted { get; set; }
        public bool ConfigCompleted { get; set; }
        public bool ExecCompleted { get; set; }
    } 

    常见的写法可能是这样

                Temp t = new Temp();
                if (t.CalculationCompleted && t.CollectionCompleted && t.ConfigCompleted && t.ExecCompleted)
                {
                    Console.WriteLine("所有属性值都为True");
                }

    这样子写没有毛病, 但是如果要判断的属性变多了呢,比如几十个,那么用这种方法写显而易见,代码量很大,而且只要Temp的属性增加了,就需要重新修改if判断,很不灵活。

    下面换一种写法

    public static bool IsAllCompleted<T>(T obj)
    {
        if (obj == null)
        {
            return false;
        }
    
        Type t = typeof(T);
        //获取属性的集合
        PropertyInfo[] pros = t.GetProperties();
        foreach (var p in pros)
        {
            if (p.Name.Contains("Completed"))
            {
                //获取属性值
                bool isCollectionCompleted = (bool)p.GetValue(obj, null);
                if (!isCollectionCompleted)
                {
                    //只要有一个数据为false就直接返回
                    return false;
                }
            }
        }
        return true;
    }
    
    
    Temp t = new Temp();
    if (IsAllCompleted<Temp>(t))
    {
        Console.WriteLine("所有属性值都为True");
    }

    这种写法通过反射获取对象中所有属性,并获取值,然后循环判断值是否为false,显而易见,通过反射不需要管类中有多少个属性,不管是新增的属性还是删除的属性 ,只需要一个循环就可以得到所有属性的值,可以说非常灵活。

    动态生成Sql

    下面写一个我们经常写的Sql语句 

    Insert into Temp(CalculationCompleted,CollectionCompleted,ConfigCompleted,ExecCompleted) Values(@CalculationCompleted,@CollectionCompleted,@ConfigCompleted,@ExecCompleted) 

    这是一个参数化的插入Sql,如果表字段比较少,那这么写还好,但是如果很多,而且表字段不固定,那么这么写就不灵活了。

    下面通过运用反射来动态生成Sql

    首先需要这个表的实体类,我们还用Temp类

    封装两个方法

    /// <summary>
    /// 返回属性名称(name, name, name)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static string GetParas<T>()
    {
        StringBuilder columns = new StringBuilder();
        Type t = typeof(T);
        PropertyInfo[] pros = t.GetProperties();
        foreach (var item in pros)
        {
            if (item.PropertyType.Name == "IList`1" || item.PropertyType.Name.ToUpper().Contains("LIST"))
            {
                continue;
            }
            columns.Append(item.Name).Append(",");
        }
        return columns.Remove(columns.Length - 1, 1).ToString();
    }
    
    /// <summary>
    /// 返回属性名称(@name,@name,@name)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static string GetATParas<T>()
    {
        StringBuilder columns = new StringBuilder();
        Type t = typeof(T);
        PropertyInfo[] properties = t.GetProperties();
        foreach (var p in properties)
        {
            if (p.PropertyType.Name == "IList`1" || p.PropertyType.Name.ToUpper().Contains("LIST"))
            {
                continue;
            }
            columns.Append("@").Append(p.Name).Append(",");
        }
        return columns.Remove(columns.Length - 1, 1).ToString();
    } 
    
    
    
    
    string para = GetParas<Temp>();
    string atPara = GetATParas<Temp>();
    
    Console.WriteLine("Insert into Temp(" + para + ") Values(" + atPara + ")");

    这样写,如果Temp表中新增的字段,那么只要实体类重新生成一下就可以了,sql语句完全不需要修改

    动态调用类中的方法

    先创建一个类

    public class Temp
    {
        public bool CalculationCompleted { get; set; }
        public bool CollectionCompleted { get; set; }
        public bool ConfigCompleted { get; set; }
        public bool ExecCompleted { get; set; }
    
        public bool Calculation(DateTime time)
        {
            return true;
        }
    
        public bool Collection(DateTime time)
        {
            return true;
        }
    
        public bool Config(DateTime time)
        {
            return true;
        }
    
        public bool Exec(DateTime time)
        {
            return true;
        } 
    }

    常见的调用类方法

    Temp t = new Temp();
    DateTime time = new DateTime();
    t.CalculationCompleted = t.Calculation(time);
    t.CollectionCompleted = t.Collection(time);
    t.ConfigCompleted = t.Config(time);
    t.ExecCompleted = t.Exec(time); 

    这样子明显很不灵活。如果类成员属性、方法增加就需要在重新写N条t.XXXCompleted=t.XXX(time)

    反射执行类方法

    public static void ReflectExecMethod<T>(T obj)
    {
        string strReslutMsg = string.Empty;
    
        Type t = typeof(T);
        PropertyInfo[] properties = t.GetProperties();
        foreach (var p in properties)
        {
            //获取属性值
            bool isCollectionCompleted = (bool)p.GetValue(obj, null);
            //判断属性值是否为false,如果是则执行方法
            if (!isCollectionCompleted)
            {
                //方法需要的参数集合
                object[] args = new object[] { DateTime.Now };
                //获取方法名
                string strMethodName = p.Name.Split(new string[] { "Completed" }, StringSplitOptions.RemoveEmptyEntries)[0];
                //执行方法得到结果
                bool result = (bool)t.GetMethod(strMethodName).Invoke(obj, args);
                //赋值
                p.SetValue(obj, result, null);
            }
        }
    }
    
    
    
    Temp t = new Temp();
    ReflectExecMethod<Temp>(t);

    通过反射调用类成员方法,并赋值 。。这样子写的好处,如果类成员方法、成员属性后面在增加了,主要符合规则,上面的代码就不需要修改。

    原文链接:https://blog.csdn.net/cxhgg/article/details/80322929

    通过反射调用类的私有成员:

    Person p = new Person();
    Type tPerson = p.GetType();
    //得到私有字段的值:
    FieldInfo privateField = tPerson.GetField("privateFields", BindingFlags.Instance | BindingFlags.NonPublic);
    //设置私有成员的值:
    privateField.SetValue(p, "修改后的私有字段值");
    Console.WriteLine(privateField.GetValue(p));
    
    //得到私有属性的值: 
    PropertyInfo privateProp = tPerson.GetProperty("PrivateProperties", BindingFlags.Instance | BindingFlags.NonPublic);
    //设置私有属性的值:
    privateProp.SetValue(p, "修改后的私有属性值",null);
    Console.WriteLine(privateProp.GetValue(p,null));
    
    //调用私有方法
    MethodInfo privateMethod = tPerson.GetMethod("SayHi", BindingFlags.Instance | BindingFlags.NonPublic);
    privateMethod.Invoke(p, null); 

     

     

     

  • 相关阅读:
    约瑟夫
    用过的ps操作
    guns框架试用笔记
    让使用WebForm的.aspx文件写的WebApi能够跨域访问
    DevExpress的GridView的行变和列变
    SSMS18.0缺少调试功能
    EF_CodeFirst框架版本问题
    微信小程序框架了解2---js的写法
    微信小程序框架了解1---总体了解
    Chrome浏览器写代码片段的地方
  • 原文地址:https://www.cnblogs.com/zhaoyl9/p/12055526.html
Copyright © 2020-2023  润新知