- 加载dll
- 遍历程序集
- 通过反射创建对象
- 通过反射调用方法
- 通过反射调用属性
反射是.net中的一种重要机制,通过反射操作,我们可以得到程序集(dll)的成员信息(namespace,class,method,properties,field,constructor等)。我们经常通过反射来操作程序集中的方法,属性,字段的赋值,实例化等等。
1.加载dll
生成一个程序集(AssemblyTest.Interface|GitHub代码),引进工程中,然后加载这个程序集:
using System;
using System.Reflection;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
try
{
//使用Assembly.Load的方式加载程序集,不需要后缀
Assembly assembly = Assembly.Load("AssemblyTest.Interface");
//使用Assembly.LoadFile的方式加载程序集,参数需要完整的路径以及文件类型(需要文件后缀),LoadFile方法不加载以来项
Assembly assemblyLoadFile = Assembly.LoadFile(@"F:\Practice\AssemblyPractice\AssemblyTest.Interface\bin\Debug\AssemblyTest.Interface.dll");
//使用Asseblly.LoadFrom的方式加载程序及,参数可以为完整路径,也可以为程序集名称(需要文件后缀)
Assembly assemblyLoadFromFullPath = Assembly.LoadFrom(@"AssemblyTest.Interface.dll");
Assembly assemblyLoadFromFileName = Assembly.LoadFrom(@"F:\Practice\AssemblyPractice\AssemblyTest.Interface\bin\Debug\AssemblyTest.Interface.dll");
Console.WriteLine(assembly.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
======================================================================================
输出:AssemblyTest.Interface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
2.遍历程序集
在加载完程序集后,可以打印程序集的信息,但是具体信息,需要通过特定的方法来拿取。我们基于AssemblyTest.Interface创建一个新的程序集(AssemblyTest.AssemblyInfo|GitHub代码),然后查看有关他的一些方法:
assemblyTest.AssemblyInfo:
AssemblyTest.AssemblyInfo
=================================================================================
using System;
using AssemblyTest.Interface;
namespace AssemblyTest.AssemblyInfo
{
public class AssemblyInformation : InterfaceParent
{
public static string publicName { get; set; }
private static string privateName { get; set; }
private int age;
public string publicFields;
public AssemblyInformation()
{
Console.WriteLine("这里是无参构造函数");
}
public AssemblyInformation(string strName, int intAge)
{
publicName = strName;
age = intAge;
Console.WriteLine("这里是有参构造函数, name={0}, age = {1}", publicName, age);
}
public void Query()
{
Console.WriteLine("Query: 当前程序集 {0}", this.GetType());
}
public void ShowMsg()
{
Console.WriteLine("ShowMsg(): 当前程序集 {0}", this.GetType());
}
public void ShowMsg1(string str)
{
Console.WriteLine("ShowMsg1(): 当前程序集 {0}, 传入参数:{1}, 类型:{2}", this.GetType(), str, str.GetType());
}
public void ShowMsg2(string str, int num)
{
Console.WriteLine("ShowMsg2(): 当前程序集 {0}, 传入参数:{1} {2}, 类型:{3}{4}", this.GetType(), str, str.GetType(), num, num.GetType());
}
public void ShowMsg3()
{
Console.WriteLine("ShowMsg3(): 当前程序集 {0}", this.GetType());
}
public void ShowMsg3(string str)
{
Console.WriteLine("ShowMsg3(): 当前程序集 {0}, 传入参数:{1}, 类型:{2}", this.GetType(), str, str.GetType());
}
public void ShowMsg3(string str, int num)
{
Console.WriteLine("ShowMsg3(): 当前程序集 {0}, 传入参数:{1} {2}, 类型:{3}{4}", this.GetType(), str, str.GetType(), num, num.GetType());
}
private void ShowMsg4()
{
Console.WriteLine("ShowMsg4(): 私有方法:{0}", this.GetType());
}
public static void ShowMsg5()
{
Console.WriteLine("ShowMsg5(): 静态方法");
}
public void ShowMsg6<T>(T t)
{
Console.WriteLine("ShowMsg6(): 泛型方法 泛型参数类型{0}", typeof(T));
}
}
}
program.cs
using System;
using System.Reflection;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
try
{
#region 加载程序集
setTitle("加载程序集");
//使用Assembly.Load的方式加载程序集,不需要后缀
//Assembly assembly = Assembly.Load("AssemblyTest.Interface");
Assembly assembly = Assembly.Load("AssemblyTest.AssemblyInfo");
//使用Assembly.LoadFile的方式加载程序集,参数需要完整的路径以及文件类型(需要文件后缀),LoadFile方法不加载以来项
Assembly assemblyLoadFile = Assembly.LoadFile(@"F:\Practice\AssemblyPractice\AssemblyTest.Interface\bin\Debug\AssemblyTest.Interface.dll");
//使用Asseblly.LoadFrom的方式加载程序及,参数可以为完整路径,也可以为程序集名称(需要文件后缀)
Assembly assemblyLoadFromFullPath = Assembly.LoadFrom(@"AssemblyTest.Interface.dll");
Assembly assemblyLoadFromFileName = Assembly.LoadFrom(@"F:\Practice\AssemblyPractice\AssemblyTest.Interface\bin\Debug\AssemblyTest.Interface.dll");
Console.WriteLine(assembly.ToString());
#endregion 加载程序集
#region 遍历属性
setTitle("遍历程序集属性");
foreach (var moudle in assembly.GetModules())
{
Console.WriteLine("Module: " + moudle.FullyQualifiedName);
}
//遍历私有方法
foreach (var item1 in item.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
Console.WriteLine(item1.Name);
}
foreach (var type in assembly.GetTypes())
{
Console.WriteLine("Type: " + type.FullName);
var infoType = assembly.GetType(type.ToString());
//构造函数
foreach (var Constructor in infoType.GetConstructors())
{
Console.WriteLine("Constructor: " + Constructor.ToString());
}
//字段
foreach (var field in infoType.GetFields())
{
Console.WriteLine("Field: " + field.Name);
}
//方法
foreach (var method in infoType.GetMethods())
{
Console.WriteLine("method: " + method.Name);
}
//属性
foreach (var properties in infoType.GetProperties())
{
Console.WriteLine("properties: " + properties.Name);
}
#endregion 遍历属性
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
private static void setTitle(string str)
{
Console.WriteLine('\n' + str.PadLeft(40, '=') + "====================================");
}
}
}
===========================================================================
输出:
===================================加载程序集====================================
AssemblyTest.AssemblyInfo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
=================================遍历程序集属性====================================
Module: F:\GitHub\AssemblyPractice\AssemblyPractice\bin\Debug\AssemblyTest.AssemblyInfo.dll
Type: AssemblyTest.AssemblyInfo.AssemblyInformation
Constructor: Void .ctor()
Constructor: Void .ctor(System.String, Int32)
Field: publicFields
method: get_publicName
method: set_publicName
method: Query
method: ShowMsg
method: ToString
method: Equals
method: GetHashCode
method: GetType
properties: publicName
3.创建对象
无参构造函数
如果把AssemblyTest.AssemblyInfo.AssemblyInformation.cs看作一个普通的类,那么我们在实例化的时候是:
using System;
using System.Reflection;
using AssemblyTest.AssemblyInfo;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
AssemblyInformation assemblyInfo = new AssemblyInformation();
assemblyInfo .Query();
}
}
}
通过反射达到相同的效果:
using System;
using System.Reflection;
using AssemblyTest.AssemblyInfo;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
//加载程序集
assembly = Assembly.Load("AssemblyTest.AssemblyInfo");
//获取程序中指定的Type对象,这个对象名称我们可以根据上方的GetTypes()方法遍历出来
Type typeTest = assembly.GetType("AssemblyTest.AssemblyInfo.AssemblyInformation");
//实例化
object oTest = Activator.CreateInstance(typeTest);
//调用 这里应为oTest为object类型,所以需要强制作为父类或者本身的Type都行
((AssemblyTest.AssemblyInfo.AssemblyInformation)oTest).Query();
((AssemblyTest.Interface.InterfaceParent)oTest).Query();
}
}
}
简单工厂+反射+配置文件=ioc(控制反转)
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。 这里简单的来理解就是动态的加载不同程序集。
建立一个类SimpleFactory:
using System;
using System.Configuration;
using System.Reflection;
namespace AssemblyPractice
{
public class SimpleFactory
{
public static AssemblyTest.Interface.InterfaceParent CreateConstructor()
{
Assembly assembly = Assembly.Load("AssemblyTest.AssemblyInfo");
Type assemblyType = assembly.GetType("AssemblyTest.AssemblyInfo.AssemblyInformation");
object oTest = Activator.CreateInstance(assemblyType);
return (AssemblyTest.Interface.InterfaceParent)oTest;
}
}
}
将实例化步骤封装SimpleFactory。我们在使用数据库的时候,一般会把数据库连接存入到App.config中,这样做的目的就是为了在更换数据库的时候不需要修改代码,同样在这里我们可以把一些会变动的参数写在App.config文件中:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<appSettings>
<add key="AssemblyType" value="AssemblyTest.AssemblyInfo,AssemblyTest.AssemblyInfo.AssemblyInformation" />
</appSettings>
</configuration>
在appSettings记录一个名为AssemblyType值为"AssemblyTest.AssemblyInfo,AssemblyTest.AssemblyInfo.AssemblyInformation"的节点。用法上类似于字典通过key值查找value值。
SimpleFactory改为:
using System;
using System.Configuration;
using System.Reflection;
namespace AssemblyPractice
{
public class SimpleFactory
{
//在读取配置文件的时候我们需要引用System.Configuration
private static string assemblyType = ConfigurationManager.AppSettings["AssemblyType"];
//通过key值来得到对用的value值
private static string assemblyName = assemblyType.Split(',')[0];
private static string assemblyClassName = assemblyType.Split(',')[1];
public static AssemblyTest.Interface.InterfaceParent CreateConstructor()
{
//加载程序集
Assembly assembly = Assembly.Load(assemblyName);
//获取程序中指定的Type对象,这个对象名称我们可以根据上方的GetTypes()方法遍历出来
Type assemblyType = assembly.GetType(assemblyClassName);
object oTest = Activator.CreateInstance(assemblyType);
return (AssemblyTest.Interface.InterfaceParent)oTest;
}
}
}
我们在program中调用:
using System;
using System.Reflection;
using AssemblyTest.AssemblyInfo;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
var Constructor = SimpleFactory.CreateConstructor();
Constructor.Query();
}
}
}
那么这么做的意义在哪里,我们再基于AssemblyTest.Interface创建工程AssemblyTest.IocTest|GitHub代码:
using System;
using AssemblyTest.Interface;
namespace AssemblyTest.IocTest
{
public class AssemblyIocInformation : InterfaceParent
{
public static string publicName { get; set; }
private static string privateName { get; set; }
private int age;
public string publicFields;
public AssemblyIocInformation()
{
Console.WriteLine("这里是无参构造函数");
}
public AssemblyIocInformation(string strName, int intAge)
{
publicName = strName;
age = intAge;
Console.WriteLine("这里是无参构造函数, name={0}, age = {1}", publicName, age);
}
public void Query()
{
Console.WriteLine("Query: 当前程序集 {0}", this.GetType());
}
public void ShowMsg()
{
Console.WriteLine("ShowMsg(): 当前程序集 {0}, name:{1}, age:{2}", this.GetType(), publicName, age);
}
}
}
SimpleFactory.cs不需要修改,修改配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<appSettings>
<!--<add key="AssemblyType" value="AssemblyTest.AssemblyInfo,AssemblyTest.AssemblyInfo.AssemblyInformation" />-->
<add key="AssemblyType" value="AssemblyTest.IocTest,AssemblyTest.IocTest.AssemblyIocInformation" />
</appSettings>
</configuration>
执行结果:
这里是无参构造函数
Query: 当前程序集 AssemblyTest.IocTest.AssemblyIocInformation
可以看到只是修改了配置文件我们就做到了不同程序集的替换。
有参构造函数
对于有参数的构造函数,我们只需要在 Activator.CreateInstance的时候将参数传入就可以。
using System;
using System.Reflection;
using AssemblyTest.AssemblyInfo;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
//加载程序集
assembly = Assembly.Load("AssemblyTest.AssemblyInfo");
//获取程序中指定的Type对象,这个对象名称我们可以根据上方的GetTypes()方法遍历出来
Type typeTest = assembly.GetType("AssemblyTest.AssemblyInfo.AssemblyInformation");
//实例化 无参
object oTest = Activator.CreateInstance(typeTest);
//有参
oTest = Activator.CreateInstance(typeTest, new object[] { "param1", 123 });
((AssemblyTest.Interface.InterfaceParent)oTest).Query();
}
}
}
======================================================================================
输出结果:
这里是无参构造函数, name=param1, age = 123
Query: 当前程序集 AssemblyTest.AssemblyInfo.AssemblyInformation
当然这里要先确保实例化对象中包含有参构造函数,而且需要确保参数类型相同。我们可以通过之前讲过的GetConstructor方法得到构造函数以及参数类型。
私有构造函数
AssemblyTest.AssemblyInfo中含有私有类Singleton:
AssemblyTest.AssemblyInfo.Singleton.cs
================================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AssemblyTest.AssemblyInfo
{
public class Singleton
{
private Singleton()
{
Console.WriteLine("私有构造函数:{0}", this.GetType());
}
}
}
==================================================================================
program.cs
==================================================================================
//调用私有方法
var privateType = assembly.GetType("AssemblyTest.AssemblyInfo.Singleton");
Activator.CreateInstance(privateType, true);
===================================================================================
输出:
私有构造函数:AssemblyTest.AssemblyInfo.Singleton
创建泛型
AssemblyTest.AssemblyInfo中含有泛型类GenericClass:
using System;
namespace AssemblyTest.AssemblyInfo
{
public class GenericClass<T>
{
public GenericClass()
{
Console.WriteLine("泛型类:{0}", this.GetType());
}
}
}
创建泛型的方法为:
using System;
using System.Reflection;
using AssemblyTest.AssemblyInfo;
using AssemblyTest.Interface;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
Assembly assembly = Assembly.Load("AssemblyTest.AssemblyInfo");
//1.泛型类型占位符(`1)
Type typeTest = assembly.GetType("AssemblyTest.AssemblyInfo.GenericClass`1");
//2.泛型在构造之前需要给予参数类型
typeTest = typeTest.MakeGenericType(typeof(int));
Activator.CreateInstance(typeTest);
}
}
}
===============================================================================
输出:
泛型类:AssemblyTest.AssemblyInfo.GenericClass`1[System.Int32]
泛型跟其他类型不太一样,在讲泛型的时有将泛型打印出来,可以看到泛型和List,dictionary都会有占位符的存在,所以在getType的时候需要把占位符也加上。在创建前需要给定泛型参数的类型。
4.调用实例方法、静态方法、重载方法
调用方法
using System;
using System.Reflection;
using AssemblyTest.AssemblyInfo;
using AssemblyTest.Interface;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
Assembly assembly = Assembly.Load("AssemblyTest.AssemblyInfo");
Type typeTest = assembly.GetType("AssemblyTest.AssemblyInfo.AssemblyInformation");
oTest = Activator.CreateInstance(typeTest);
//方法名称我们可以通过遍历来得到
//无参方法ShowMsg()
var methodTest = typeTest.GetMethod("ShowMsg");
methodTest.Invoke(oTest,null);
//ShowMsg1(string str)
methodTest = typeTest.GetMethod("ShowMsg1");
methodTest.Invoke(oTest, new object[] { "str" });
//ShowMsg2(sting,int)
methodTest = typeTest.GetMethod("ShowMsg2");
methodTest.Invoke(oTest, new object[] { "str", 123 });
//ShowMsg3() 重载,无参
methodTest = typeTest.GetMethod("ShowMsg3", new Type[] { });
methodTest.Invoke(oTest, null);
//ShowMsg3() 重载,参数为string
methodTest = typeTest.GetMethod("ShowMsg3", new Type[] { typeof(string) });
methodTest.Invoke(oTest, new object[] { "string" });
//ShowMsg4() 无参私有方法
methodTest = typeTest.GetMethod("ShowMsg4", BindingFlags.NonPublic | BindingFlags.Instance);
methodTest.Invoke(oTest, null);
//ShowMsg5() 静态方法
methodTest = typeTest.GetMethod("ShowMsg5");
methodTest.Invoke(null, null);
//ShowMsg6() 泛型方法!!!!!!!!!!!!
methodTest = typeTest.GetMethod("ShowMsg6");
methodTest = methodTest.MakeGenericMethod(typeof(int));
methodTest.Invoke(oTest, new object[] { 123 });
}
}
}
==================================================================================
输出:
ShowMsg(): 当前程序集 AssemblyTest.AssemblyInfo.AssemblyInformation
ShowMsg1(): 当前程序集 AssemblyTest.AssemblyInfo.AssemblyInformation, 传入参数:str, 类型:System.String
ShowMsg2(): 当前程序集 AssemblyTest.AssemblyInfo.AssemblyInformation, 传入参数:str System.String, 类型:123System.Int32
ShowMsg3(): 当前程序集 AssemblyTest.AssemblyInfo.AssemblyInformation
ShowMsg3(): 当前程序集 AssemblyTest.AssemblyInfo.AssemblyInformation, 传入参数:string, 类型:System.String
ShowMsg4(): 私有方法:AssemblyTest.AssemblyInfo.AssemblyInformation
ShowMsg5(): 静态方法
ShowMsg6(): 泛型方法 泛型参数类型System.Int32
调用属性
using System;
using System.Reflection;
using AssemblyTest.AssemblyInfo;
using AssemblyTest.Interface;
namespace AssemblyPractice
{
internal class Program
{
private static void Main(string[] args)
{
assembly = Assembly.Load("AssemblyTest.AssemblyInfo");
typeTest = assembly.GetType("AssemblyTest.AssemblyInfo.AssemblyInformation");
oTest = Activator.CreateInstance(typeTest);
foreach (var properties in typeTest.GetProperties())
{
Console.WriteLine(properties.Name);
Console.WriteLine(properties.GetValue(oTest));
if (properties.Name.Equals("publicName"))
{
properties.SetValue(oTest, "str");
}
Console.WriteLine(properties.GetValue(oTest));
}
}
}
===================================================================================
publicName
str