反射 | 程序集 | Invoke

反射之动态创建对象


什么是程序集:

程序集是。net中的概念,net中dll和exe都是程序集,他们的区别就是exe可以直接执行(因为它有入口main函数,而dll没有,)而dll 不可以直接执行,它没有入口main函数,它是供其他程序来调用的

控制台应用程序最后编译成了exe文件,而类库编译生成了dll文件

什么是反射:

反射就是通过编程的方式,动态加载程序集,并获取里面类型,并创建对象,调用其成员的过程,这就是反射


《1》 我们来首先在解决方案下创建一个类库,命名为“TestDll”  代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestDll
{
    public class Person
    {
        public Person(string name,int age,string email)
        {
            this.Name = name;
            this.Age = age;
            this.Email = email;
        }

        public Person()
        {

        }
        public string Name { get; set; }

        public int Age { get; set; }

        public string Email { get; set; }

        public void Say()
        {
            Console.WriteLine("Hi.....");
        }

        public void SayHello()
        {
            Console.WriteLine("Hi,我是SayHello无参数重载方法!");
        }

        public void SayHello(string msg)
        {
            Console.WriteLine(msg);
        }

        public int Add(int x, int y)
        {
            return x + y;
        }
    }

    public interface IFlyable
    {
        void Fly();
    }

    public class Student : Person
    { 
        
    }
    internal class Teacher : Person
    { 
    
    }
    public class MyClass1:IFlyable
    {

        public void Fly()
        {
            Console.WriteLine("");
        }
    }
    public delegate void MyDelegate();

    public struct MyStruct
    { 
    
    }
    struct MyStruct1
    { 
    
    }
}

《2》我们创建一个控制台应用程序,来练习一下这个反射如何使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace 反射
{
    public class MyClass
    {

        public MyClass()
        { 
        }
        public MyClass(string name)
        {
            this.Name = name;
        }
        public MyClass(string name, int age)
        { 
        }
        public string Name { get; set; }

        public void Say()
        {
            Console.WriteLine("你好......");
        }

        private void SayHello()
        {
            Console.WriteLine("大家好才是真的好");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {

            //对于Type的介绍  

            //1.怎么获取一个类型的Type  (即:该类型的类型原数据),办法有两种  

            //1.1当有类型的对象的时候的获取方式  

            MyClass mc = new MyClass();
            Type t1 = mc.GetType();  //这样就获得了MyClass类型的Type  

            //1.2只有类,没有类型对象的时候  
            Type t2 = typeof(MyClass); //这样就获得了MyClass类型的Type  


            //-----------------------那么拿到这个Type能干啥?-----------------------//  

            //1可以获取当前类型的父类是谁?  
            Console.WriteLine(t1.BaseType.ToString());

            //可以获取当前的类型的父类的父类是谁?  
            //Console.WriteLine(t1.BaseType.BaseType.ToString());  

            //2获取当前类型中的所有公有字段  
            FieldInfo[] fieldinfo = t1.GetFields();

            //3获取当前类型中的所有公有属性  
            PropertyInfo[] propertyinfo = t1.GetProperties();

            //4获取当前类型中的所有公有方法  
            MethodInfo[] methodinfo = t1.GetMethods();

            //5获取当前类型中的所有公有事件  
            EventInfo[] eventinfo = t1.GetEvents();

            //6获取当前类型中的所有公有构造函数  
            ConstructorInfo[] constructorinfo = t1.GetConstructors();

            //6.1获取当前类型中无参数的构造函数
            var a1 = t1.GetConstructor(new Type[] { });

            //6.2获取当前类型中带一个string类型的构造函数
            var a2 = t1.GetConstructor(new Type[] { typeof(string) });

            //6.3获取当前类型中带一个string类型和一个带int类型的构造函数
            var a3 = t1.GetConstructor(new Type[] { typeof(string), typeof(int) });

            //6.4 调用无参的构造函数
            a1.Invoke(new Object[] { });
            //或者这样也可以
            var a11= Activator.CreateInstance(t1);

            //6.5 调用当前类型中带一个string类型的构造函数
            a2.Invoke(new Object[] { "中国" });

            //6.6 调用当前类型中带一个string类型和一个带int类型的构造函数
            a3.Invoke(new Object[] { "中国", 62 });

            var ss = t1.GetProperties();

            //7 获取构造函数中的所有参数
            foreach (ConstructorInfo c in constructorinfo)
            {
                ParameterInfo[] ps = c.GetParameters();//获取构造函数的参数
                foreach (ParameterInfo pi in ps)
                {
                    Console.WriteLine("参数类型:" + pi.ParameterType.ToString() + " 参数名称:" + pi.Name);
                }
            }

            

            
            

            //................等等  









            //---------------------------------------------//  

            //动态加载程序集  (Assembly类表示一个程序集,它是一个抽象的类,它里面有些静态成员)  
            //根据程序的路径,动态加载一个程序集  
            Assembly asm = Assembly.LoadFile(@"D:\学习\Solution1\TestDll\bin\Debug\TestDll.dll");

            //获取该程序集中的所有类型  
            Type[] types = asm.GetTypes();

            foreach (var v in types)
            {
                //Console.WriteLine(v.FullName); //获取该程序集中所有类型的名称,打印产生如下输出  
                /* 
                 TestDll.Person 
                 TestDll.IFlyable 
                 TestDll.Student 
                 TestDll.Teacher 
                 TestDll.MyClass1 
                 TestDll.MyDelegate 
                 TestDll.MyStruct 
                 TestDll.MyStruct1                 
                 */
            }


            //我们一般我们只获取程序集中的所有公有类型(即:获取程序集中所有public的类型)  
            Type[] publicTypes = asm.GetExportedTypes();

            foreach (var v in publicTypes)
            {
                Console.WriteLine(v.FullName); //打印一下,产生如下输出  
                /* 
                 TestDll.Person 
                 TestDll.IFlyable 
                 TestDll.Student 
                 TestDll.MyClass1 
                 TestDll.MyDelegate 
                 TestDll.MyStruct 
                 */
            }

            //-------有时候我们只需要获取某个类型 比如我们现在想获取这个Person类型-------------//  


            Type ptype = asm.GetType("TestDll.Person"); //注意:括号中应该带上Person类的命名空间TestDll; 即:命名空间+类名  



            //-------------------现在我们来调用Person类中的Say()方法---------//  


            //想调用Person类中的Say()方法就先获取这个Say()方法  
            MethodInfo methodA = ptype.GetMethod("Say");

            //拿到了这个Say()方法了,然后我们就调用这个Say()方法....可以怎么调用呢?  

            //methodinfo.Invoke这个方法是用来调用Say()这个方法的,可是它里面有两个参数,  
            //第一个参数:表示Say()这个方法所属的类的对象; 因为Say()方法是非静态方法,非静态方法只有类对象才能调用(如果是调用静态方法就这个参数就写成null就可以了)  
            //第二个参数:表示Say()这个方法的参数,如果没有参数就写个null就可以了。因为一个方法可以有多个参数,所以它是一个object的数组类型  

            //问题来了,既然我们想得到Say()这个方法所属的类的对象,我们又法直接方法Person类型,所以不能直接new一个Person类型的对象,我们用另外一种方法来创建Person类型的对象  
            object objA = Activator.CreateInstance(ptype); //这样就会根据这个ptype创建了Person类的一个对象  

            methodA.Invoke(objA, null); //在这里就是调用了这个Say方法 输出:Hi.....  




            //---------------现在我们来调用Person类中的SayHello()的无参数重载方法------------//  

            MethodInfo methodB = ptype.GetMethod("SayHello", new Type[] { }); //注意 第二个参数表示SayHello()这个方法的参数,因为方法可以有多个参数,所有第二个参数是一个数组,而我在数组里什么都没有放,即放了一个空数组,就表示获取Person类下面没有参数的SayHello()方法  
            object objB = Activator.CreateInstance(ptype);
            methodB.Invoke(objB, null);


            //--------------现在我们来调用Person类中的SayHello()有1个参数的重载方法---------//  

            MethodInfo methodC = ptype.GetMethod("SayHello", new Type[] { typeof(string) }); //获取SayHello()这个方法中有一个string类型的参数的方法  
            object objC = Activator.CreateInstance(ptype);
            methodC.Invoke(objC, new object[] { "参数1" }); //输出:参数1  








            //-------------------------通过Type来创建对象----------------//  


            //我们上面有样式过 根据Person的Type创建一个Person类型对象  
            object objD = Activator.CreateInstance(ptype);

            //但是通过以上这种方式来创建对象也是有一点问题的。因为如果这个Person没有无参构造函数,只有一些有参数的构造函数,我想通过调用一些有参数的构造函数来创建对象,这个时候使用Activator.CreateInstance(ptype);就不行了,因为这里Activator.CreateInstance(ptype);就是调用无参构造函数创建对象的,它没发指定有参构造函数来创建对象,所以我们只能通过另外一种办法来创建  


            //-------通过调用指定的构造函数来创建对象---------  

            //1.获取指定的构造函数(这个构造函数有三个参数,第一个是string类型,第二个是int类型,第三个是string类型)  
            ConstructorInfo info = ptype.GetConstructor(new Type[] { typeof(string), typeof(int), typeof(string) });

            //2.调用构造函数来创建对象  
            object objE = info.Invoke(new object[] { "张三", 25, "123@qq.com" }); //这个objeE就是通过指定构造函数来创建的对象  

            //通过放射获取指定对象的属性的值 比如这里我要获取Person类的Name属性值  

            //1.首先的获取这个Person类下面的Name属性  
            PropertyInfo pinfo = ptype.GetProperty("Name");

            string name = pinfo.GetValue(objE, null).ToString(); //获取objE这个对象的Name属性值  

            Console.WriteLine(name);

            //同样我们也可以设置属性的值  
            pinfo.SetValue(objE, "李四"); //设置objE这个对象的Name属性值  

            string name2 = pinfo.GetValue(objE, null).ToString();

            Console.WriteLine(name2); //输出一下重新设置的Name值  







            //-------------------Type类的IsAssignableFrom()方法---------------------------//  

            //其实就是检查一个类是否是另外一个类的子类  

            Type typePerson = asm.GetType("TestDll.Person");
            Type typeStudent = asm.GetType("TestDll.Student");
            Type typeTeacher = asm.GetType("TestDll.Teacher");

            //表示能否将typeStudent类型的对象赋值给typePerson类型的对象(其实就是检查Student类是否是Person类的子类)  
            bool p = typePerson.IsAssignableFrom(typeStudent);
            Console.WriteLine(p); //输出:true  


            bool s = typePerson.IsAssignableFrom(typeTeacher);
            Console.WriteLine(s); //输出:true  

            //表示能否将typeStudent类型的对象赋值给typeTeacher类型的对象  
            bool t = typeTeacher.IsAssignableFrom(typeStudent);
            Console.WriteLine(t);//输出;false  



            //-------------------Type类的IsInstanceOfType()方法---------------------------//  

            //其实就是检查一个对象是否是某个类型的实例  

            object objPerson = Activator.CreateInstance(typePerson); //创建一个typePerson类型的对象  
            object objStudent = Activator.CreateInstance(typeStudent);//创建一个typeStudent类型的对象  
            object objTeache = Activator.CreateInstance(typeTeacher);//创建一个typeTeacher类型的对象  


            //检查objPerson是否是TypePerson类型的对象  
            bool p1 = typePerson.IsInstanceOfType(objPerson);
            Console.WriteLine(p1); //输出:true  

            //检查objStudent是否是TypePerson类型的对象  
            bool s1 = typePerson.IsInstanceOfType(objStudent);
            Console.WriteLine(s1);//输出:true,因为Student是Person类的子类  

            //检查objTeache是否是TypePerson类型的对象  
            bool tt = typePerson.IsInstanceOfType(objTeache);
            Console.WriteLine(tt);//输出:true,因为Teache是Person类的子类  

            bool ts = typeTeacher.IsInstanceOfType(objStudent);
            Console.WriteLine(ts); //输出False ,因为objStudent不是Teacher的实例  



            //-------------------Type类的IsSubclassOf()方法---------------------------//  

            //其实就检查一个类是否是另外一个类的子类 (验证的是父子类关系,与接口无关)  

            //检查typePerson类是否是typeStudent类的子类 (确定当前 System.Type 表示的类是否是从指定的 System.Type 表示的类派生的。)  
            bool b1 = typePerson.IsSubclassOf(typeStudent);
            Console.WriteLine(b1); //输出:false  

            //检查typeStudent类是否是typePerson类的子类  
            bool b2 = typeStudent.IsSubclassOf(typePerson);
            Console.WriteLine(b2);//输出:true  



            //-------------------Type类的IsSubclassOf()方法---------------------------//  

            //其实就是判断一个类是否是抽象类  

            //检查Person类是否为抽象类  
            bool bb = typePerson.IsAbstract;

            Console.WriteLine(bb); //输出:false  

            Console.ReadKey();

        }
        
    }
}


反射之动态创建对象


//1.2获取浏览器请求资源的的文件名(不带扩展名)即:获取该文件对应的后台类的名称

string className = Path.GetFileNameWithoutExtension(context.Request.RequestUrl);

 

//1.3.1获取当前类的命名空间

string ns=  MethodBase.GetCurrentMethod().DeclaringType.Namespace;

 

//1.3获取当前正在执行的程序集(即:获取Default类所在的程序集)

//CreateInstance()方法的意思是:从当前正在执行的程序集中查找指定的类型,并使用系统激活器,创建它的实例

//注:CreateInstance()方法的参数是一个类的完全限定名;即:命名空间.类名,所以我们需要在1.3.1中获取当前类的命名空间

IHttpHandler objPage = (IHttpHandler)Assembly.GetExecutingAssembly().CreateInstance(ns+"." + className);


----------------------------------------------

//2.1.1获取当前执行的exe的路径(即:获取项目下MyIISServer.exe这个文件的路径)

//Assembly表示一个程序集 GetExecutingAssembly方法表示获取包含当前执行的代码的程序集。Location属性:获取包含清单的已加载文件的路径或 UNC 位置。

//通过调试得知:p1的值为:H:\asp.net\Solution1\MyIISServer\bin\Debug

string p1 = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

 

//用户请求的静态资源的实际文件路径

//通过调试得知fileName的值为:H:\asp.net\Solution1\MyIISServer\bin\Debug\web\HtmlPage1.html

string fileName = Path.Combine(Path.Combine(p1, "web",context.Request.RequestUrl.TrimStart('/')));


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Mapping
{
    public class EntityToEntity
    {
        
        public static TResult EntityMap<TSource, TResult>(TSource model)
        {
          

            object resultInstance = Activator.CreateInstance<TResult>();//创建目标类型对象

            PropertyInfo[] sourceProperties = typeof(TSource).GetProperties(); //获取源类型对象的所有公有属性
            PropertyInfo[] resultProperties = typeof(TResult).GetProperties(); //获取目标类型对象的所有公有属性

            foreach (var resuPropert in resultProperties)
            {
                //如果sourceProperties里面的属性名称与resultProperties里面的属性名称一致,就获取sourceProperties里面的这个属性
                var source = sourceProperties.FirstOrDefault(r => r.Name.Equals(resuPropert.Name, StringComparison.OrdinalIgnoreCase));
                if (source != null)
                {
                    //获取这个属性的值
                    var val = source.GetValue(model, null);

                    //如果两个属性的类型一致,则赋值
                    if (source.PropertyType == resuPropert.PropertyType)
                    {
                        //将val的值赋給resuPropert属性
                        resuPropert.SetValue(resultInstance, val, null);
                        continue;
                    }

                    //如果是一个普通类
                    if (resuPropert.PropertyType.IsClass)   
                    {
                        //1.0、获取指定方法名称的泛型方法
                        var map = typeof(EntityToEntity).GetMethod("EntityMap");
                        

                        //2.0、创建一个对应泛型类型的非泛型反射方法  
                        //map = map.MakeGenericMethod(source.PropertyType, resuPropert.PropertyType); //这种写法,或下面这种写法都可以

                        //通过map的MakeGenericMethod(new Type[] { source.PropertyType,resuPropert.PropertyType })方法,创建一个非泛型版本的方法,并且返回了表示结果的构造方法的MethodInfo的对象(这个目标类型其实就是resuPropert.PropertyType ),其中new Type[] {  source.PropertyType,resuPropert.PropertyType }是用来替换泛型类型 TSource, TResult的具体类型。
                        //(实际就是:将方法转换成具有指定类型参数的 MethodInfo 对象。)
                        map = map.MakeGenericMethod(new Type[] {source.PropertyType, resuPropert.PropertyType });//泛型方法有几个类型参数,这个就是几个参数,如果传入一个参数则会报错: 此类型或方法有 2 个泛型参数,但只提供了 1 个泛型变量。必须为每个泛型参数都提供一个泛型变量。

                        //调用这个带参数的构造函数来创建目标类型的对象( resuPropert.PropertyType类型的对象)。它的参数是一个 val (这个val其实就是属性都值。注意:这个属性其实是一个对象)
                        var temp = map.Invoke(null, new[] { val });

                        resuPropert.SetValue(resultInstance, temp, null);
                        continue;
                    }

                    //这里判断对象的属性是否是数组或者集合
                    //这个这个判断的写法我更是偷懒了。你自己去研究怎么判断吧
                    if (resuPropert.PropertyType.Name.StartsWith("List"))
                    {
                        var map = typeof(EntityToEntity).GetMethod("EntityMap");
                        map = map.MakeGenericMethod(source.PropertyType.GetGenericArguments()[0], resuPropert.PropertyType.GetGenericArguments()[0]);

                        var gobj = Activator.CreateInstance(resuPropert.PropertyType);
                        var add = resuPropert.PropertyType.GetMethod("Add");

                        var count = (int)source.PropertyType.GetProperty("Count").GetValue(val, null);
                        var get = source.PropertyType.GetMethod("get_Item");
                        for (int i = 0; i < count; i++)
                        {
                            var item = get.Invoke(val, new object[] { i });
                            var o = map.Invoke(null, new[] { item });
                            add.Invoke(gobj, new object[] { o });
                        }
                        resuPropert.SetValue(resultInstance, gobj, null);
                    }                    
                }
            }
            return (TResult)resultInstance;
        }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Mapping
{
    public class Test
    {
        public static TResult EntityMap<TResult>(object obj)
        {


            var resultInstance = Activator.CreateInstance<TResult>(); //创建TResul类型的对象

            PropertyInfo[] resultPros = typeof(TResult).GetProperties(); //获取TResult类型的公共属性

            PropertyInfo[] sourcePros = obj.GetType().GetProperties(); //获取数据源的公共属性

            foreach (var result in resultPros)
            {
                var source = sourcePros.FirstOrDefault(r => r.Name.Equals(result.Name, StringComparison.OrdinalIgnoreCase));
                if (source != null)
                {
                    var sourceValue = source.GetValue(obj);

                 

                    if (source.PropertyType == result.PropertyType)
                    {
                        result.SetValue(resultInstance, sourceValue, null);
                        continue;
                    }

                    if (result.PropertyType.IsClass)
                    {
                        var mapMethod = typeof(Test).GetMethod("EntityMap");
                        mapMethod = mapMethod.MakeGenericMethod(result.PropertyType);//注意:这里是反射创建泛型方法(参数是目标类型,其实就是EntityMap<TResule>)这个泛型方法
                        var temp = mapMethod.Invoke(null, new[] { sourceValue }); //调用泛型方法,创建目标类对象(Invoke是自调用)

                        result.SetValue(resultInstance, temp, null);
                        continue;
                    }
                }
            }
            return (TResult)resultInstance;
        }
    }
}


假如一个对象有20个属性,当我们创建一个新的对象的时候,想复制这个对象的所有属性的时候,如果一个一个的赋值,太麻烦。所有可以通过以下方式进行拷贝

//通过反射来拷贝一份对象
static object MyClone(object obj)
{
    Type type = obj.GetType();
    object newobj = Activator.CreateInstance(type);
    foreach (PropertyInfo pro in type.GetProperties()) //获取所有属性
    {
        if (pro.CanRead && pro.CanWrite) //如果这个属性值是可读又是可写的
        {
            var value = pro.GetValue(obj);//获取obj对象的属性值
            pro.SetValue(newobj, value); //把值赋值给newobj对象对应的属性
        }
    }
    return newobj;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值