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:\online7\20160928Advanced7Course2Reflection\MyReflection\MyReflection\bin\Debug\Ruanmou.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); 

 

 

 

posted @ 2019-12-17 17:04  智者见智  阅读(510)  评论(0编辑  收藏  举报