.Net学习之反射

反射

  • .Net提供的关于反射的内容都包含在System.Reflection/System.Type…命名空间下

DLL的组成

编译完成之后DLL实际包含2个部分IL代码Metadata

  • IL代码

    .class public auto ansi serializable beforefieldinit System.Object
    {
    .custom instance void System.Runtime.InteropServices.ClassInterfaceAttribute::.ctor(valuetype System.Runtime.InteropServices.ClassInterfaceType) = (
        01 00 02 00 00 00 00 00
    )
    .custom instance void System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = (
        01 00 01 00 00
    )
    .custom instance void __DynamicallyInvokableAttribute::.ctor() = (
        01 00 00 00
    )
    }

  • Metadata

    • DLL名称
    • 接口名称
    • 命名空间
    • 类名称

    • 上面这些都是IL组成的Metadata,它用来描述Dll的构成部分
  • 反射.Net框架提供给我读取DLL等类库中Metadata(编译高级语言所产生的IL和Metadata)的信息.

反射加载DLL

满足CLI(Common Language Infrastructure)(通用语言基础架构)的DLL,都可以使用反射加载.

  • 托管与非托管
    有GC的一般为托管代码,没有GC机制一般为非托管代码.
正常方法(Common非反射)
  • 添加应用

  • 创建实例调用

    //创建实例 调用
    DBHelper dbHelper = new DBHelper();
    dbHelper.Id = 1;
    dbHelper.Name = "仗劍走天涯";
    dbHelper.Query();

通过反射加载DLL
//1 动态加载     默认加载当前路径的dll文件,不需要后缀
Assembly assembly = Assembly.Load("Ruanmou.DB.Sqlserver");
// 必须是完整路径
Assembly assembly1 = Assembly.LoadFile(@"E:\online7\20160928Advanced7Course2Reflection\MyReflection\MyReflection\bin\Debug\Ruanmou.DB.Sqlserver.dll");//LoadFile不会加载依赖项,必须是完整路径
// 可以是当前路径  也可以是完整路径
Assembly assembly2 = Assembly.LoadFrom("Ruanmou.DB.Sqlserver.dll");
Assembly.LoadFrom(@"E:\online7\20160928Advanced7Course2Reflection\MyReflection\MyReflection\bin\Debug\Ruanmou.DB.Sqlserver.dll");
读取module、类、方法、特性
foreach (var item in assembly.GetModules())
{
    //一般为DLL的全路径名称??
    //当多个类库编译为一个DLL文件时,一个类库就会时一个模块
    Console.WriteLine(item.FullyQualifiedName);
}
foreach (var item in assembly.GetTypes())
{
    //获得下面所有的类型
    Console.WriteLine(item.FullName);
}
Type typeDBHelper = assembly.GetType("Ruanmou.DB.Sqlserver.DBHelper");//2 获取类型 (获取类型信息的方式不止一个)
foreach (var item in typeDBHelper.GetConstructors())
{
    //获取构造函数的名称,构造函数没有名称,统一都是".ctor"
    Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetProperties())
{
    Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetFields())//字段
{
    //相对于属性,没有getset访问器
    Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetAttributes())
{
    //获取特性  
    Console.WriteLine(item.Name);
}
foreach (var item in typeDBHelper.GetMethods())
{
    //包含父类的方法,包含属性的get set
    Console.WriteLine(item.Name);
}

通过反射创建对象实例

通过反射创建对象
//创建引用DLL
Assembly assembly = Assembly.Load("Ruanmou.DB.Sqlserver");
//获取类型  当然还有很多别的方法
Type typeDBHelper = assembly.GetType("Ruanmou.DB.Sqlserver.DBHelper")
//创建对象
object oDBHelper = Activator.CreateInstance(typeDBHelper);
//创建不是调用默认构造函数的类
Activator.CreateInstance(typeTest, "demon");//调用带有一个string类型的构造函数
Activator.CreateInstance(typeTest, 11, "限量版(397-限量版)");//类似上面的

//调用
//可以使用强制类型转换直接调用,但是这失去了反射加载DLL的意义
DBHelper oDBHelperSqlserver = (DBHelper)oDBHelper;//因为通常情况下,不知道类型是什么样的,也无法动态加载
oDBHelperSqlserver.Query();

//源程序和DLL实现了相同的接口,则可以实现面向接口的通信,可以实现动态加载
IDBHelper dbHelperReflection = (IDBHelper)oDBHelper;
dbHelperReflection.Query();
反射+简单工厂+配置文件==(IOC依赖注入/控制反转)

虽然再上述场景中可以实现动态的调用,但是再实际的生产环境中仍然面临者不够灵活的缺点

//IDBHelper接口
public interface IDBHelper
{
    void Query();
}
//创建一个简单工厂模式,封装DLL的调用
public class SimpleFactory
{
    //读取配置文件
    private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelper"];

    public static IDBHelper CreateDBHelper()
    {
        //解析配置文件的内容
        string dllName = IDBHelperConfig.Split(',')[1];
        string className = IDBHelperConfig.Split(',')[0];
        //封装加载DLL和创建DB实例
        Assembly assembly = Assembly.Load(dllName);
        Type type = assembly.GetType(className);
        object oObject = Activator.CreateInstance(type);
        return (IDBHelper)oObject;
    }
}
<!--AppConfig内容-->
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <appSettings>
    <add key="IDBHelper" value="Ruanmou.DB.Sqlserver.DBHelper,Ruanmou.DB.Sqlserver"/>
  </appSettings>
</configuration>
//调用 实现
IDBHelper dbHelperFactory = SimpleFactory.CreateDBHelper();
dbHelperFactory.Query();
//再上述的过程中 实现了DB类库的动态加载
//再需要新增,修改等业务场景中无需修改代码,即可实现修改业务逻辑的操作
通过反射破坏单例*

即可以调用类的私有的构造函数

//一个带有私有构造函数的类 一个简单的单例模式的类
/// <summary>
/// 单例模式
/// </summary>
public sealed class Singleton
{
    private Singleton()
    {
        Console.WriteLine("初始化一次");
    }

    private static Singleton Instance = new Singleton();

    public static Singleton CreateInstance()
    {
        return Instance;
    }
}
//调用
Type typeSingleton = assembly.GetType("Ruanmou.DB.Sqlserver.Singleton");
//第二个参数为Bool nonpublic,设置为True 可以调用私有的构造函数
Activator.CreateInstance(typeSingleton, true);
通过反射创建泛型类
//再名称中需要指定泛型中的占位符  `1--一个参数  `3--3个参数
Type typeGeneric = assembly.GetType("Ruanmou.DB.Sqlserver.GenericClass`2");
//创建实例之前,需要指定创建实例的参数类型,该方法会返回一个修改过后的类型
typeGeneric = typeGeneric.MakeGenericType(typeof(int),typeof(string));
//需要以修改过后的类型来创建实例
Activator.CreateInstance(typeGeneric);

通过反射调用方法

//创建一个类有各种方法可被调用
public class ReflectionTest
{
    public int Id { get; set; }
    public string Name { get; set; }

    public string Field = null;
    public static string FieldStatic = null;


    #region 构造函数
    public ReflectionTest()
    {
        Console.WriteLine("这里是{0}无参数构造函数", this.GetType());
    }

    public ReflectionTest(string name)
    {
        Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
    }

    public ReflectionTest(int id, string name)
    {
        Console.WriteLine("这里是{0} 有参数构造函数", this.GetType());
    }
    #endregion

    public static void ShowStatic(string name)
    {
        Console.WriteLine("这里是{0}的ShowStatic", typeof(ReflectionTest));
    }

    public void ShowGeneric<T>(string name)
    {
        Console.WriteLine("这里是{0}的ShowStatic  T={1}", this.GetType(), typeof(T));
    }

    public void Show1()
    {
        Console.WriteLine("这里是{0}的Show1", this.GetType());
    }

    public void Show2(int id)
    {

        Console.WriteLine("这里是{0}的Show2", this.GetType());
    }

    public void Show3()
    {
        Console.WriteLine("这里是{0}的Show3_1", this.GetType());
    }

    public void Show3(int id, string name)
    {
        Console.WriteLine("这里是{0}的Show3", this.GetType());
    }

    public void Show3(string name, int id)
    {
        Console.WriteLine("这里是{0}的Show3_2", this.GetType());
    }

    public void Show3(int id)
    {

        Console.WriteLine("这里是{0}的Show3_3", this.GetType());
    }

    public void Show3(string name)
    {

        Console.WriteLine("这里是{0}的Show3_4", this.GetType());
    }

    private void Show4(string name)
    {
        Console.WriteLine("这里是{0}的Show4", this.GetType());
    }

}
反射调用构造方法
Type typeTest = assembly.GetType("Ruanmou.DB.Sqlserver.ReflectionTest");
object oTest = Activator.CreateInstance(typeTest);
Activator.CreateInstance(typeTest, "demon");
Activator.CreateInstance(typeTest, 11, "限量版(397-限量版)");
Activator.CreateInstance(typeTest,new object {"demon"});
//系统CreateInstance方法
public static object CreateInstance(Type type, params object[] args);
反射调用实例方法/静态方法/私有方法/泛型方法(MVC控制器采用的模式)
Type typeTest = assembly.GetType("Ruanmou.DB.Sqlserver.ReflectionTest");
//创建对象的实例
object oTest = Activator.CreateInstance(typeTest);

foreach (var item in typeTest.GetMethods())
{
    Console.WriteLine(item.Name);
}
{
    //调用无参数的方法
    MethodInfo method = typeTest.GetMethod("Show1");
    method.Invoke(oTest, null);
}
{
    //调用带一个int参数的方法
    MethodInfo method = typeTest.GetMethod("Show2");
    method.Invoke(oTest, new object[] { 11 });
}
{
    //调用静态方法
    MethodInfo method = typeTest.GetMethod("ShowStatic");
    //调用静态方法的时候可以不传递实例
    method.Invoke(null, new object[] { "KOBE→Bryant" });
}
//调用重载方法需要指定参数类型,给GetMethod方法传递一个Type数组
{
    //调用无参数的重载方法
    MethodInfo method = typeTest.GetMethod("Show3", new Type[] { });
    method.Invoke(oTest, null);
}
//调用又参数的重载方法,类型需要一致
{
    MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(int) });
    method.Invoke(oTest, new object[] { 11 });
}
{
    MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(string) });
    method.Invoke(oTest, new object[] { "限量版(397-限量版)" });
}
{
    MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(string), typeof(i
    method.Invoke(oTest, new object[] { "书呆熊@拜仁", 22 });
}
{
    MethodInfo method = typeTest.GetMethod("Show3", new Type[] { typeof(int), typeof(stri
    method.Invoke(oTest, new object[] { 33, "不懂微软" });
}
//调用私有方法,需要传递一个枚举类型BindingFlags 的Instance和.NonPublic
{
    MethodInfo method = typeTest.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic)
    method.Invoke(oTest, new object[] { "有木有" });
}
//调用泛型方法,和调用泛型实例类似,需要指定泛型的的类型;(不需要占位符)
{
    MethodInfo method = typeTest.GetMethod("ShowGeneric");
    method = method.MakeGenericMethod(typeof(string));
    method.Invoke(oTest, new object[] { "有木有" });
}

反射字段和属性,分别获取值/设置值

//用于反射的类
public class People
{
    public People()
    {
        Console.WriteLine("{0}被创建", this.GetType().FullName);
    }

    public int Id { get; set; }
    public string Name { get; set; }
}
//创建实例
Type typeTest = assembly.GetType("Ruanmou.DB.Sqlserver.ReflectionTest");
object oTest = Activator.CreateInstance(typeTest);
//foreach (var item in typeTest.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
//{
//    Console.WriteLine(item.Name);
//}
foreach (var prop in typeTest.GetProperties())//获取所有的属性
{
    Console.WriteLine(prop.GetValue(oTest));//PropertyInfo.GetValue(实例) 获取属性值
    Console.WriteLine(prop.Name);
    if (prop.Name.Equals("Id"))
    {
        prop.SetValue(oTest, 22);//PropertyInfo.SetValue(实例,值的Object对象) 设置属性值
    }
    else if (prop.Name.Equals("Name"))
    {
        prop.SetValue(oTest, "Bond(331-object)");
    }
    //PropertyInfo.Propertytype  返回属性的类型

    Console.WriteLine(prop.GetValue(oTest));
}
//反射读取属性,设置属性的意义(灵活,简洁)
Using(SqlConnection sqlconn = new SqlConnection(connstring))
{
    SqlCommand cmd = new SqlCommand(querystring,sqlconn);
    sqlconn.Open();
    DataReader reader = cmd.ExcuteDataReader();
    User oUser = new User();
    Type type = typeof(oUser);
    if(reader.Read())
    {
        foreach(var item in type.GetProperties())
        {
            //简介的从Sql查询中赋值
            //当然还可以加入依赖关系(极简ORM)
            item.SetValue(oUser,reader[item.Name]);
        }
    }
}

反射的好处和局限

  • 动态灵活
  • 代码复杂,性能损耗(大约是普通方法的几倍到几十倍,百万次大约几十毫秒级)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值