-
什么是反射
反射是通过程序了解及调用“程序集”中程序相关的属性、字段、方法。
C#中程序集分两种:*.dll 和 *.exe两种。
-
反射的作用
1、通过反射我们可以读取程序集(*.dll 和 *.exe)中代码内容
2、可以根类名(字符串),来动态创建类的对象
3、可以动态获取类对象中的属性、字段和方法
4、可以根据类中方法名(字符串)调用该方法
- 反射常用类
1、Type类:可以获取类信息(即:获取类包含的成员与方法)
功能:使用Type对象查看一个类中所有的非私有字段、方法、属性
2、Assambly类:得到一个程序集中(反射)信息
功能:加载程序集
Assambly.Load(); //推荐使用
Assambly.LoadFrom("程序集的完整路径名");
下面建立一个控制台程序对这两个的类的用法进行演示
定义一个Person类,我们用上述反射常用的两个类获取这个Person中的信息:
using System;
using System.Collections.Generic;
using System.Text;
namespace 反射学习
{
public class Person
{
//字段
public string Name;
public int Age;
private int _ID;
//属性
public int ID
{
get
{
return _ID;
}
set
{
_ID = value;
}
}
//方法
public void DisplayName()
{
Console.WriteLine(Name);
}
public void DisplayAge()
{
Console.WriteLine(Age);
}
public void DisplayID()
{
Console.WriteLine(ID);
}
/// <summary>
/// 无参方法
/// </summary>
public void Test1()
{
Console.WriteLine("Person类 无参方法 Test1");
}
/// <summary>
/// 有参方法
/// </summary>
/// <param name="str"></param>
public void Test2(string str)
{
Console.WriteLine("Person类 有参方法 Test2,参数为=" + str);
}
/// <summary>
/// 无参方法
/// </summary>
public void Test3()
{
Console.WriteLine("Person类 无参方法 Test3");
}
/// <summary>
/// 重载方法
/// </summary>
/// <param name="num"></param>
public void Test3(int num)
{
Console.WriteLine("有参方法Test3, 参数为=" + num);
}
}
}
使用Type类获取Person类中的信息(方法、属性、字段)
using System;
using System.Reflection;
namespace 反射学习
{
class Program
{
/// <summary>
/// Type 查询类的普通信息
/// </summary>
public void Test()
{
Person person = new Person();
Type typeObj = person.GetType();
//显示类的名称
Console.WriteLine(typeObj.Name);
//显示类命名空间
Console.WriteLine(typeObj.Namespace);
//显示类所属的程序集
Console.WriteLine(typeObj.Assembly);
}
/// <summary>
/// Type 查询类中的方法、属性、字段等信息
/// </summary>
public void Test2()
{
Person person = new Person();
Type typeObj = person.GetType();
//得到字段信息
FieldInfo[] fieldInfos = typeObj.GetFields();
foreach (FieldInfo item in fieldInfos)
{
Console.WriteLine(item.Name);
}
Console.WriteLine("------------------------");
//得到方法信息
MethodInfo[] methodInfos = typeObj.GetMethods();
foreach (MethodInfo item in methodInfos)
{
Console.WriteLine(item.Name);
}
Console.WriteLine("------------------------");
//得到属性信息
PropertyInfo[] propertyInfos = typeObj.GetProperties();
foreach (PropertyInfo item in propertyInfos)
{
Console.WriteLine(item.Name);
}
Console.WriteLine("------------------------");
}
static void Main(string[] args)
{
Program pg = new Program();
pg.Test();
pg.Test2();
}
}
}
使用Assembly类获取程序集中Person信息(类)
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace 反射学习
{
class Program2
{
/// <summary>
/// 通过程序集中的一个类的实例化,从而得到这个“程序集”的对象
/// </summary>
public void Test1()
{
Person person = new Person();
//得到程序集
Assembly assemblyObj = person.GetType().Assembly;
//得到程序集的全名
Console.WriteLine(assemblyObj.FullName);
Type[] typeArray = assemblyObj.GetTypes();
foreach (Type item in typeArray) //这里会输出三个类:Person、Program、Program2
{
Console.WriteLine(item.Name);
}
}
/// <summary>
/// 通过路径,直接访问“程序集”(*.dll)
/// </summary>
public void Test2()
{
//直接通过路径得到程序集对象
Assembly assObj = Assembly.LoadFrom(@"D:\反射学习.dll");
Type[] typeArray = assObj.GetTypes();
foreach (Type item in typeArray) //这里会输出三个类:Person、Program、Program2
{
Console.WriteLine(item.Name);
}
}
static void Main1(string[] args)
{
Program2 pg = new Program2();
//pg.Test1();
pg.Test2();
}
}
}
Test1()方法是通过Person对象获取该类所在的程序集,再通过该程序集获取获取所有类
Test2()方法是直接读取dll程序集中的所有类
使用Assembly类获取程序集中指定类和指定的有参/无参方法及重载方法
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace 反射学习
{
class Program3
{
/// <summary>
/// 演示:调用程序集中类的无参方法
/// </summary>
public void Test1()
{
string className = "Person"; //类名称
string methodName = "Test1"; //方法名称
//得到程序集对象
Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
//得到Type
Type type= assembly.GetType("反射学习." + className);
//实例化对象
object obj = Activator.CreateInstance(type);
//得到方法
MethodInfo methodInfo = type.GetMethod(methodName);
//调用无参方法
methodInfo.Invoke(obj,null); //null表示该方法无参
}
/// <summary>
/// 演示:调用程序集中类的有参方法和重载方法
/// </summary>
public void Test2()
{
string className = "Person";
string methodName1 = "Test2"; //Person类中Test2有参方法
Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
Type type = assembly.GetType("反射学习." + className);
object obj = Activator.CreateInstance(type);
//Person类中Test2有参方法调用
MethodInfo methodInfo1 = type.GetMethod(methodName1);
object[] param1 = new object[1] { "123" };
methodInfo1.Invoke(obj, param1);
}
/// <summary>
/// 演示:调用程序集中类的有参方法和重载方法
/// </summary>
public void Test3()
{
string className = "Person";
string methodName2 = "Test3"; //Person类中Test3无参方法
string methodName3 = "Test3"; //Person类中Test3有参重载方法
Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
Type type = assembly.GetType("反射学习." + className);
object obj = Activator.CreateInstance(type);
//Person类中Test3无参方法调用
MethodInfo methodInfo2 = type.GetMethod(methodName2, new Type[0]); //注意:有重载的无参方法,要加上new Type[0],否则程序不知道你要找的是哪一个
methodInfo2.Invoke(obj, null);
//Person类中Test3重载方法调用
Type[] typeArrayPara = { typeof(int) };
MethodInfo methodInfo3 = type.GetMethod(methodName3, typeArrayPara); //注意:有重载的有参方法,要加上{ typeof(int) },才能调用,否则程序报错
object[] param2 = new object[1] { 456 };
methodInfo3.Invoke(obj, param2);
}
/// <summary>
/// 演示:调用程序集中的属性
/// </summary>
public void Test4()
{
string className = "Person";
string propertyName = "ID";
Assembly assembly = Assembly.LoadFrom(@"D:\反射学习.dll");
Type type = assembly.GetType("反射学习." + className);
object obj = Activator.CreateInstance(type);
//获得属性
PropertyInfo propertyInfo = type.GetProperty(propertyName);
//给属性赋值
propertyInfo.SetValue(obj, 23);
//获得属性值
string value = propertyInfo.GetValue(obj, null).ToString();
Console.WriteLine(value);
}
static void Main(string[] args)
{
Program3 pg = new Program3();
//pg.Test1();
//pg.Test2();
//pg.Test3();
pg.Test4();
}
}
}
Test1():演示的是调用Person类中的无参发方法
Test2():演示的是调用Person类中的有参方法
Test3()和Test4():演示的是调用Person类中的重载方法
下面再额外演示反射三个功能:
1、使用反射查看谁是谁的父类,或者是否继承了接口
2、使用反射查看一个类是否是另一个类的实例
3、使用反射查看一个类是否为另一个类的子类
先新建几个类:Men(父类)、ZhangSan(子类)、Dog(不相关的类)、ISpeak(接口)
Men.cs(父类):
/***************
*
* Title:反射学习
*
* Description:
* 该类是个父类--人
*
* Author:王亚东
*
* Date:2024
*
* Modify:
*
* ****/
using System;
using System.Collections.Generic;
using System.Text;
namespace 反射学习
{
class Men
{
}
}
ZhangSan.cs(子类):
注意:此类继承了Men.cs和ISpeak接口
/***************
*
* Title:反射学习
*
* Description:
* 该类是个子类,继承父类和接口
*
* Author:王亚东
*
* Date:2024
*
* Modify:
*
* ****/
using System;
using System.Collections.Generic;
using System.Text;
namespace 反射学习
{
class ZhangSan:Men,ISpeak
{
}
}
Dog.cs(不相关类)
注:此类是一个不相关类,只是为了演示需要
/***************
*
* Title:反射学习
*
* Description:
* 该类是一个普通,与其他父类和子类都不相关
*
* Author:王亚东
*
* Date:2024
*
* Modify:
*
* ****/
using System;
using System.Collections.Generic;
using System.Text;
namespace 反射学习
{
class Dog
{
}
}
ISpeak.cs(接口类)
/*************
*
*
* 接口:说话接口,由其他类继承
*
*
*
*
* *************/
using System;
using System.Collections.Generic;
using System.Text;
namespace 反射学习
{
interface ISpeak
{
}
}
下面互相查看这几个的关系:
using System;
using System.Collections.Generic;
using System.Text;
namespace 反射学习
{
class Program4
{
//查看谁是谁的父类,或者是否继承了接口
public void Test1()
{
Type typeMen = typeof(Men);
Type typeZhangSan = typeof(ZhangSan);
Type typeDog = typeof(Dog);
Type typeInterface = typeof(ISpeak);
Console.WriteLine(typeMen.IsAssignableFrom(typeZhangSan)); //判断Men是否是ZhangSan类的父类,返回T
Console.WriteLine(typeMen.IsAssignableFrom(typeDog)); //判断Men是否是Dog类的父类,返回F
//接口判断
Console.WriteLine(typeInterface.IsAssignableFrom(typeZhangSan)); //T
Console.WriteLine(typeInterface.IsAssignableFrom(typeDog)); //F
}
//一个类是否为另一个类的实例
public void Test2()
{
Type typeMen = typeof(Men);
Type typeInterface = typeof(ISpeak);
Men menObj = new Men();
ZhangSan zhangsanObj = new ZhangSan();
Dog dogObj = new Dog();
Console.WriteLine(typeMen.IsInstanceOfType(menObj)); //menObj是否是typeMen的实例,返回T
Console.WriteLine(typeMen.IsInstanceOfType(zhangsanObj)); //zhangsanObj是否是typeMen的实例,返回T
Console.WriteLine(typeMen.IsInstanceOfType(dogObj));//dogObj是否是typeMen的实例,返回F
//判断接口
Console.WriteLine(typeInterface.IsInstanceOfType(menObj));//F
Console.WriteLine(typeInterface.IsInstanceOfType(zhangsanObj));//T
}
//一个类是否为另一个类的子类
public void Test3()
{
Type typeMen = typeof(Men);
Type typeZhangSan = typeof(ZhangSan);
Type typeDog = typeof(Dog);
Type typeInterface = typeof(ISpeak);
Console.WriteLine(typeZhangSan.IsSubclassOf(typeMen)); //判断ZhangSan类是否是Men的子类,返回T
Console.WriteLine(typeDog.IsSubclassOf(typeMen)); //判断ZhangSan类是否是Men的子类,返回F
}
static void Main(string[] args)
{
Program4 pro4 = new Program4();
//pro4.Test1();
//pro4.Test2();
pro4.Test3();
}
}
}
Test1():查看谁是谁的父类,或者谁继承了接口
Test2(): 查看一个类是否是另一个类的实例
Test3(): 查看一个类是否为另一个类的子类
- 什么是特性
特性是一种允许我们向程序的程序集中增加元数据的语言结构,提供了一种将声明性信息与代码关联起来的途径
- 两个特性:Obsolete特性(即:“过时”特性)和Conditional系统特性(即:“条件”特性)
- Obsolete特性(即:“过时”特性):版本升级后API变更,对一些旧的API方法进行提示或强制更换,例如:在将Unity2018的项目升级到Unity2020版本后,API会有一些绿色下划线提示"某个方法已经过时,请用xxxx方法代替",或者直接红色下划线强制更换
代码如下
using System;
namespace 学习特性
{
class Program
{
[Obsolete("此方法已过时,请用NewMethod方法替代",false)] //第二个参数默认为false,非必须的,只是提示警告;当第二个参数设置为true时,此方法必须被替换,不替换会报错
public void OldMethod()
{
Console.WriteLine("这是一个旧方法");
}
public void NewMethod()
{
Console.WriteLine("新的方法");
}
public void Test()
{
OldMethod();
NewMethod();
}
static void Main(string[] args)
{
Program proObj = new Program();
proObj.Test();
}
}
}
[Obsolete("此方法已过时,请用NewMethod方法替代",false)] ://第二个参数默认为false,非必须的,只是提示警告;当第二个参数设置为true时,此方法必须被替换,不替换会报错 (红色下划线提示)
- Conditional系统特性(即:“条件”特性): 一般用于程序开发过程中的“测试方法”的编写。测试阶段定义“测试的宏”,发布商业版本,则取消宏即可。 适用条件:针对方法
代码如下
#define TestOnly
#define debug
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace 学习特性
{
class Program2
{
//特性:条件执行,默认不执行,只有在有“宏”的特定条件才执行
[Conditional("TestOnly")] //加上这个特性就不会被调用了,只能加上“宏”(上面的#define TestOnly )才能被调用到,此特性是为了在测试环境或者编辑器环境下用的
public void Test1()
{
Console.WriteLine("这是一个测试方法");
}
public void Method1()
{
Console.WriteLine("这是方法1");
}
public void Method2()
{
Console.WriteLine("这是方法2");
}
/// <summary>
/// 学习演示“条件编译”
/// </summary>
public void Test2()
{
Console.WriteLine("aaaa");
Console.WriteLine("bbbb");
#if debug
Method1();
Console.WriteLine("cccc"); //此代码不被执行,除非加了“宏”
#else
Method2();
Console.WriteLine("dddd"); //加了“宏”(即#define debug)之后此行代码不被执行,上一行就会被执行
#endif
}
public static void Main(string[] args)
{
Program2 pro2 = new Program2();
//pro2.Test1();
//pro2.Method1();
//pro2.Method2();
pro2.Test2();
}
}
}
Test1():加了 [Conditional("TestOnly")]特性后,该方法默认是不会被执行的,只能加上“宏”(上面的#define TestOnly )才能被调用到
Test2():默认只执行#else和#endif之间的代码,除非加上“宏"(上面的#if debug)才会被执行
结语:好了,今天的反射和特性就讲解到这里,同学们若有疑问请在评论区留言