C#反射(Reflection)

GitHub代码 | CSDN代码

  • 加载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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值