反射学习

实现步骤:
导入using System.Reflection;
Assembly.Load(“程序集”)加载程序集,返回类型是一个Assembly
foreach (Type type in assembly.GetTypes())
{
string t = type.Name;
}
得到程序集中所有类的名称
Type type = assembly.GetType(“程序集.类名”);获取当前类的类型
Activator.CreateInstance(type); 创建此类型实例
MethodInfo mInfo = type.GetMethod(“方法名”);获取当前方法
mInfo.Invoke(null,方法参数);

知识点1—Assembly类

1)要引用的程序集和需要程序集的程序在同一目录时,不需要加载程序集,直接可以获取其中的类型.
2)要引用的程序集合需要程序集不在同一目录中时,先要加载程序集,才能获取其中的类和类的方法等。

加载程序集

Assembly assembly = Assembly.LoadFrom("Graphic.dll");//加载程序集(cs文件生成.dll文件,.dll文件是一个程序集)
Console.WriteLine("name of assembley:"+assembly.GetName());//输出程序集的Name

根据类名创建类型的一个实例

 object myobj =assembly.CreateInstance(nsp+classname,true, BindingFlags.Default, null, ars, null, null);

知识点2—Type类(获得实例对象的成员信息)

  Type mytype = myobj.GetType();
  if (myobj!=null)
    Console.WriteLine("<{0}>类名:{1}",classList.Count,mytype.Name);
            
  MethodInfo[] ms = mytype.GetMethods();
    Console.WriteLine("  {0}的方法个数:{1}", mytype.FullName, ms.Length);
  for (int i=0;i<ms.Length;i++)
    Console.WriteLine("  ({0}){1}", i+1,ms[i].Name);

  FieldInfo[] fs = mytype.GetFields();
  Console.WriteLine("  {0}的字段个数:{1}", mytype.FullName, fs.Length);
  for (int i = 0; i < fs.Length; i++)
  Console.WriteLine("  ({0}){1}", i+1, fs[i].Name);

知识点3—调用方法

[方法名].Invoke([实例对象], [实参])

知识点4—为什么要用反射?

反射+配置文件
看过大话设计模式的,都知道在抽象工厂模式中.小菜在大鸟的指点下,用简单工厂,再用反射,再用配置文件,一步步将抽象工厂改装, 使得抽象工厂的缺点和个各类之间的耦合性都大大降低.

在不用反射+配置文件时,抽象工厂的问题是:
*如果需要增加多个数据库,就要增加多个类,更麻烦。

在使用反射+配置文件后,抽象工厂的改变是:
*如果需要更换数据库,不需改变代码,只需要修改配置文件中DB的值。

小结:反射技术的使用去除了switch或if,解除分支判断的耦合。在这里,反射可以说是简化了工厂的实现。

解决问题

假设我们有以下的泛型方法

public T fun<T>(){
    return obj = new T();
}

如果T有很多,那通常的方法是使用swich进行判断

var className = "string";
var obj = new object();
swich(className){
    case "string":
        obj =fun<string>();
        break;
    case "int":
        obj =fun<int>();
        break;
        ...
}

解决方案
上面的写法会导致大量的冗余代码。
我们想要如下的解决方案,通过传入类的名字来动态的调用fun函数

//错误的方案
public object ExcuteMethod(string className){
    var type = typeof(className);
    return fun<type>();
}

泛型中的T是类型,而这里使用的type是参数。没有找到办法把类型当作参数传递。但是可以换一种思路,通过反射来直接执行函数。
以下是通过放射实现动态执行泛型函数

//获取当前程序集
Assembly mockAssembly = Assembly.GetExecutingAssembly();
//可以使用LoadFrom(URL)方法加载其他程序集(.dll文件),url是绝对路劲
Assembly mockAssembly = Assembly.LoadFrom("C:/test/test.dll");

//通过类名获取类,参数是类的全名,命名空间+类名,Myclass是泛型方法中fun<T>()中的T
var curType = mockAssembly.GetType("Myspacename.Myclass");

//获取方法所在类的Type,MethodClass是存放fun<T>()方法的类
var MethodType = typeof(MethodClass);
//如果是泛型类,如 MyGenericClass<T>{},要这样获得Type
var MethodType = typeof(MyGenericClass<>);

//获取泛型方法,
var GenericMethod =MethodType.GetMethod(MethodName);

//如果方法是多态的,即有多个同名方法,可以通过传入Type[]参数来找到方法
//这样可以找到方法fun(string para1,int para2)
var GenericMethod =MethodType.GetMethod(
MethodName, new Type[] {typeof(string),typeof(int)});

//合并生成最终的函数
MethodInfo curMethod =GenericMethod.MakeGenericMethod(curType);

//执行函数
//如果要执行的是静态函数,则第一个参数为null
//第二个参数为参数值
//即要调用的函数应该为 static fun<T>(string para1,int para2)
curMethod.Invoke(null, new object[]{"第一个参数的值",1});

//如果是非静态参数,第一个参数得传MethodClass的实例化对象
//即要调用的函数应该为 fun<T>(string para1,int para2)
var classobj = new MethodClass();
curMethod.Invoke(classobj, new object[]{"第一个参数的值",1});

使用缓存存储生成的方法
利用放射生成方法非常的耗费时间,所以可以考虑将生成的方法存在缓存中,以后直接执行就行,不用每次都重新生成方法

学习代码

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

namespace learn
{
    class Program
    {
         
        static void Main(string[] args)
        {
            string strClass = "learn.Test";           // 命名空间+类名
            string strMethod = "Method";        // 方法名

            Type type = Type.GetType(strClass);      // 通过类名获取同名类
            
            //方法一、使用静态类型
            Object obj = System.Activator.CreateInstance(type);       // 创建实例
            MethodInfo method = type.GetMethod(strMethod, new Type[] { typeof(string), typeof(string) });  //获取方法
            string r = (string)method.Invoke(obj, new object[] { "AAA", "BBBB" });    //执行方法
            Console.WriteLine("Method 返回值:" + r);

            //方法二、使用动态类型
            //(dynamic 动态类型对象 b 来调用反射得到对象的属性、方法可直接调用,从而省去了频繁的类型转换操作)
            dynamic dobj = System.Activator.CreateInstance(type);
            string sr = dobj.Method("AAA", "BBBB");
            Console.WriteLine("Method 返回值:" + sr);

            Console.Read();   
        }

    }

    class Test
    {
        // 无参数,无返回值方法
        public void Method()
        {
            Console.WriteLine("Method(无参数) 调用成功!");
        }

        // 有参数,无返回值方法
        public void Method(string str)
        {
            Console.WriteLine("Method(有参数) 调用成功!参数 :" + str);
        }

        // 有参数,有返回值方法
        public string Method(string str1, string str2)
        {
            Console.WriteLine("Method(有参数,有返回值) 调用成功!参数 :" + str1 + ", " + str2);
            string className = this.GetType().FullName;         // 非静态方法获取类名
            return className;
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值