反射之动态创建对象
什么是程序集:
程序集是。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;
}