21.反射

21.反射和特性-反射


21.1 知识点

知识点回顾

编译器是一种翻译程序,用于将源语言程序翻译为目标语言程序。

源语言程序:某种程序设计语言写成的,比如C#、C、C++、Java等语言写的程序。

目标语言程序:二进制数表示的伪机器代码写的程序。

程序集

程序集是经由编译器编译得到的,供进一步编译执行的那个中间产物。在WINDOWS系统中,它一般表现为后缀为.dll(库文件)或者是.exe(可执行文件)的格式。

说人话:

程序集就是我们写的一个代码集合,我们现在写的所有代码最终都会被编译器翻译为一个程序集供别人使用,比如一个代码库文件(dll)或者一个可执行文件(exe)。

元数据

元数据就是用来描述数据的数据。这个概念不仅仅用于程序上,在别的领域也有元数据。

说人话:

程序中的类,类中的函数、变量等等信息就是程序的元数据。有关程序以及类型的数据被称为元数据,它们保存在程序集中。

反射的概念

程序正在运行时,可以查看其它程序集或者自身的元数据。一个运行的程序查看本身或者其它程序的元数据的行为就叫做反射。

说人话:

在程序运行时,通过反射可以得到其它程序集或者自己程序集代码的各种信息,类,函数,变量,对象等等,实例化它们,执行它们,操作它们。

反射的作用

因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性。

  • 程序运行时得到所有元数据,包括元数据的特性。
  • 程序运行时,实例化对象,操作对象,程序运行时创建新对象,用这些对象执行任务。

反射的语法相关

测试类

class Test
{
    private int i = 1;
    public int j = 0;
    public string str = "123";

    public Test()
    {

    }

    public Test(int i)
    {
        this.i = i;
    }

    public Test(int i, string str) : this(i)
    {
        this.str = str;
    }

    public void Speak()
    {
        Console.WriteLine(i);
    }
}

Type

Type类(类的信息类)是反射功能的基础,它是访问元数据的主要方式。使用 Type 的成员获取有关类型声明的信息,包括构造函数、方法、字段、属性和类的事件。

获取Type对象

对象调用GetType()方法

int a = 42;
Type type1 = a.GetType();
Console.WriteLine(type1);//System.Int32

typeof关键字传入类名

Type type2 = typeof(int);
Console.WriteLine(type2);//System.Int32

Type.GetType()方法传入类名

// 注意:类名必须包含命名空间,不然找不到
Type type3 = Type.GetType("System.Int32");
Console.WriteLine(type3);//System.Int32
  • 注意:type1 type2 type3 指向堆空间的内存地址是一样(指向的都是同一个房间)
Assembly属性得到类的程序集信息
//Type类中的Assembly属性 可以返回类型所在程序集信息
Console.WriteLine(type1.Assembly);//System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
Console.WriteLine(type2.Assembly);//System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
Console.WriteLine(type3.Assembly);//System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
GetMembers方法获取类中的所有公共成员的反射信息
//首先得到Test类的Type
Type testType = typeof(Test);

//获取类中的所有公共成员
//调用Type类中的GetMembers()方法 这样可以得到Test类中的所有公共成员
//只可以得到所有公共成员 私有的得不到 获得其他成员时同理
//要用到MemberInfo类的数组 装载 Test类中的所有成员
//MemberInfo类是成员的反射信息类
//MemberInfo类需要引用命名空间 using System.Reflection;
MemberInfo[] infos = testType.GetMembers();
//遍历打印Test类的所有公共成员
for (int i = 0; i < infos.Length; i++)
{
    Console.WriteLine(infos[i]);
    //Void Speak()
    //System.Type GetType()
    //System.String ToString()
    //Boolean Equals(System.Object)
    //Int32 GetHashCode()
    //Void.ctor()//这是无参构造函数
    //Void.ctor(Int32)//这是有参构造函数
    //Void.ctor(Int32, System.String)//这是有参构造函数
    //Int32 j
    //System.String stringHelloWorld
}
获取类的公共构造函数的反射信息并调用

GetConstructors方法获取类的所有公共构造函数的反射信息

//调用Type类中的GetConstructors()方法 这样可以得到Test类中的所有构造函数
//要用到ConstructorInfo类的数组 装载 Test类中的所有构造函数
//ConstructorInfo类是构造函数的反射信息类
//ConstructorInfo类需要引用命名空间 using System.Reflection;
ConstructorInfo[] ctors = testType.GetConstructors();
//遍历打印Test类的所有构造函数
for (int i = 0; i < ctors.Length; i++)
{
    Console.WriteLine(ctors[i]);
    //Void.ctor()
    //Void.ctor(Int32)
    //Void.ctor(Int32, System.String)
}

GetConstructors方法传入Type数组作为参数获取类的指定公共构造函数的反射信息并Invoke传入object数组调用

//获取类的指定公共构造函数
//调用Type类中的GetConstructor()方法 传入不同的Type数组参数 Type数组中内容是按顺序是参数类型 这样可以得到Test类中的不同的构造函数
//要用到ConstructorInfo类 装载 Test类中的所有构造函数
//ConstructorInfo类是构造函数的反射信息类
//ConstructorInfo类需要引用命名空间 using System.Reflection;

//调用指定的公共构造函数
//调用ConstructorInfo类中的Invoke()方法 传入不同的object数组参数 object数组中的内容是按顺序传入的参数值 这样可以执行Test类中的不同的构造函数
//注意:调用ConstructorInfo类中的Invoke()方法得到的对象还是object类的 要把他as成Test类才能得到Test类的对象

//获取类的公共无参构造函数并调用
//调用Type类中的GetConstructor()方法 传入new Type[0]
//传入长度为0的空Type数组 意味着这个构造函数没有参数 这样可以得到Type中的公共无参构造函数
ConstructorInfo testNoParameterConstructor = testType.GetConstructor(new Type[0]);
//执行无参构造 调用ConstructorInfo类中的Invoke()方法 传入object数组
//正常来说要传入object数组 充当按顺序传入的参数值 但是由于是无参构造 没有参数 所以传null代表传入空的object数组
Test test1 = testNoParameterConstructor.Invoke(null) as Test;//执行无参构造
Console.WriteLine("test1.j:" + test1.j);//test1.j:0

//获取类的公共有参构造函数并调用

//调用Type类中的GetConstructor()方法 传入new Type[] { typeof(int) }
//代表着传入长度为1 有一个整形类型变量的数组 可以得到Type中的有一个int类型参数的有参构造
ConstructorInfo testHaveIntParameterConstructor = testType.GetConstructor(new Type[] { typeof(int) });
//执行有参构造  调用ConstructorInfo类中的Invoke()方法 传入new object[] { 2 }这个object数组 代表传入有一个int类型变量2的object数组
Test test2 = testHaveIntParameterConstructor.Invoke(new object[] { 2 }) as Test;//这样 通过构造函数 i被初始化为2了
Console.WriteLine("test2.stringHelloWorld:" + test2.str);//test2.stringHelloWorld:123

//调用Type类中的GetConstructor()方法 传入new Type[] { typeof(int), typeof(string) }
//代表着传入长度为2 有一个整形类型变量和一个string类型变量的数组 可以得到Type中的有一个int类型参数和一个string类型参数的有参构造
ConstructorInfo testHaveIntAndStringParameterConstructor = testType.GetConstructor(new Type[] { typeof(int), typeof(string) });
//执行有参构造 调用ConstructorInfo类中的Invoke()方法 传入new object[] { 4, "444444" }这个object数组 代表传入有一个int类型变量4和一个string类型变量"444444"的object数组
Test test3 = testHaveIntAndStringParameterConstructor.Invoke(new object[] { 4, "444444" }) as Test;//这样 通过构造函数 i被初始化为4了 str被初始化为444444
Console.WriteLine("test3.stringHelloWorld:" + test3.str);//test3.stringHelloWorld:444444
获取类的公共成员变量的反射信息并修改

GetFields方法获取类的所有公共成员变量的反射信息

//调用Type类中的GetFields()方法 可以得到Test类中的所有公共成员变量
//要用到FieldInfo类的数组 装载 Test类中的所有公共成员变量
//FieldInfo类是公共成员变量的反射信息类
//FieldInfo类需要引用命名空间 using System.Reflection;
FieldInfo[] fieldInfos = testType.GetFields();
//遍历打印Test类的所有公共成员变量
for (int i = 0; i < fieldInfos.Length; i++)
{
    Console.WriteLine(fieldInfos[i]);
    //Int32 j
    //System.String stringHelloWorld
}

GetField方法传入变量名获取类的指定公共成员变量的反射信息

//调用Type类中的GetField()方法 传入公共成员变量名 可以得到对应的公共成员变量
//要用到FieldInfo类装载 Test类中的公共成员变量
//FieldInfo类是公共成员变量的反射信息类
//FieldInfo类需要引用命名空间 using System.Reflection;
FieldInfo fieldInfoJ = testType.GetField("j");
Console.WriteLine(fieldInfoJ);//Int32 j

通过公共成员变量的反射信息,GetValue方法传入实例对象获取对应公共变量值,并使用SetValue方法传入实例对象和值修改指定公共成员变量的值

//new出一个测试类例子test4
Test test4 = new Test();
test4.j = 99;
test4.str = "2222";

//通过公共成员变量的反射信息 获取对象的某个变量的值
//调用FieldInfo类中的GetValue()方法 传入Test类的对象
//注意:要先得到用FieldInfo类装的指定名称的公共成员变量的反射信息
Console.WriteLine(fieldInfoJ.GetValue(test4));//99

//通过公共成员变量的反射信息 修改指定对象的某个变量的值
//调用FieldInfo类中的SetValue()方法 传入Test类的对象和要设置的值
//注意:要先得到用FieldInfo类装的指定名称的公共成员变量的反射信息
fieldInfoJ.SetValue(test4, 100);//修改指定对象的某个变量的值
Console.WriteLine(fieldInfoJ.GetValue(test4));//100
获取类的公共成员方法的反射信息并调用
//首先得到string类的Type
Type stringType = typeof(string);

GetMethods方法获取类的所有公共成员方法的反射信息

//调用Type类中的GetMethods()方法 这样可以得到string类中的所有成员方法
//要用到MemberInfo类的数组 装载 string类中的所有成员
//MethodInfo类是公共成员方法的反射信息类
//MemberInfo类需要引用命名空间 using System.Reflection;
MethodInfo[] methods = stringType.GetMethods();
//遍历打印string类的所有公共成员方法
for (int i = 0; i < methods.Length; i++)
{
    Console.WriteLine(methods[i]);

    #region string类的所有公共成员方法
    //System.String Replace(System.String, System.String)
    //System.String[] Split(Char, System.StringSplitOptions)
    //System.String[] Split(Char, Int32, System.StringSplitOptions)
    //System.String[] Split(Char[])
    //System.String[] Split(Char[], Int32)
    //System.String[] Split(Char[], System.StringSplitOptions)
    //System.String[] Split(Char[], Int32, System.StringSplitOptions)
    //System.String[] Split(System.String, System.StringSplitOptions)
    //System.String[] Split(System.String, Int32, System.StringSplitOptions)
    //System.String[] Split(System.String[], System.StringSplitOptions)
    //System.String[] Split(System.String[], Int32, System.StringSplitOptions)
    //System.String Substring(Int32)
    //System.String Substring(Int32, Int32)
    //System.String ToLower()
    //System.String ToLower(System.Globalization.CultureInfo)
    //System.String ToLowerInvariant()
    //System.String ToUpper()
    //System.String ToUpper(System.Globalization.CultureInfo)
    //System.String ToUpperInvariant()
    //System.String Trim()
    //System.String Trim(Char)
    //System.String Trim(Char[])
    //System.String TrimStart()
    //System.String TrimStart(Char)
    //System.String TrimStart(Char[])
    //System.String TrimEnd()
    //System.String TrimEnd(Char)
    //System.String TrimEnd(Char[])
    //Boolean Contains(System.String)
    //Boolean Contains(System.String, System.StringComparison)
    //Boolean Contains(Char)
    //Boolean Contains(Char, System.StringComparison)
    //Int32 IndexOf(Char)
    //Int32 IndexOf(Char, Int32)
    //Int32 IndexOf(Char, System.StringComparison)
    //Int32 IndexOf(Char, Int32, Int32)
    //Int32 IndexOfAny(Char[])
    //Int32 IndexOfAny(Char[], Int32)
    //Int32 IndexOfAny(Char[], Int32, Int32)
    //Int32 IndexOf(System.String)
    //Int32 IndexOf(System.String, Int32)
    //Int32 IndexOf(System.String, Int32, Int32)
    //Int32 IndexOf(System.String, System.StringComparison)
    //Int32 IndexOf(System.String, Int32, System.StringComparison)
    //Int32 IndexOf(System.String, Int32, Int32, System.StringComparison)
    //Int32 LastIndexOf(Char)
    //Int32 LastIndexOf(Char, Int32)
    //Int32 LastIndexOf(Char, Int32, Int32)
    //Int32 LastIndexOfAny(Char[])
    //Int32 LastIndexOfAny(Char[], Int32)
    //Int32 LastIndexOfAny(Char[], Int32, Int32)
    //Int32 LastIndexOf(System.String)
    //Int32 LastIndexOf(System.String, Int32)
    //Int32 LastIndexOf(System.String, Int32, Int32)
    //Int32 LastIndexOf(System.String, System.StringComparison)
    //Int32 LastIndexOf(System.String, Int32, System.StringComparison)
    //Int32 LastIndexOf(System.String, Int32, Int32, System.StringComparison)
    //Int32 Compare(System.String, System.String, Boolean)
    //Int32 Compare(System.String, System.String, System.StringComparison)
    //Int32 Compare(System.String, System.String, System.Globalization.CultureInfo, System.Globalization.CompareOptions)
    //Int32 Compare(System.String, System.String, Boolean, System.Globalization.CultureInfo)
    //Int32 Compare(System.String, Int32, System.String, Int32, Int32)
    //Int32 Compare(System.String, Int32, System.String, Int32, Int32, Boolean)
    //Int32 Compare(System.String, Int32, System.String, Int32, Int32, Boolean, System.Globalization.CultureInfo)
    //Int32 Compare(System.String, Int32, System.String, Int32, Int32, System.Globalization.CultureInfo, System.Globalization.CompareOptions)
    //Int32 Compare(System.String, Int32, System.String, Int32, Int32, System.StringComparison)
    //Int32 CompareOrdinal(System.String, System.String)
    //Int32 CompareOrdinal(System.String, Int32, System.String, Int32, Int32)
    //Int32 CompareTo(System.Object)
    //Int32 CompareTo(System.String)
    //Boolean EndsWith(System.String)
    //Boolean EndsWith(System.String, System.StringComparison)
    //Boolean EndsWith(System.String, Boolean, System.Globalization.CultureInfo)
    //Boolean EndsWith(Char)
    //Boolean Equals(System.Object)
    //Boolean Equals(System.String)
    //Boolean Equals(System.String, System.StringComparison)
    //Boolean Equals(System.String, System.String)
    //Boolean Equals(System.String, System.String, System.StringComparison)
    //Boolean op_Equality(System.String, System.String)
    //Boolean op_Inequality(System.String, System.String)
    //Int32 GetHashCode()
    //Int32 GetHashCode(System.StringComparison)
    //Int32 GetHashCode(System.ReadOnlySpan`1[System.Char])
    //Int32 GetHashCode(System.ReadOnlySpan`1[System.Char], System.StringComparison)
    //Boolean StartsWith(System.String)
    //Boolean StartsWith(System.String, System.StringComparison)
    //Boolean StartsWith(System.String, Boolean, System.Globalization.CultureInfo)
    //Boolean StartsWith(Char)
    //System.String Concat(System.Object)
    //System.String Concat(System.Object, System.Object)
    //System.String Concat(System.Object, System.Object, System.Object)
    //System.String Concat(System.Object[])
    //System.String Concat[T](System.Collections.Generic.IEnumerable`1[T])
    //System.String Concat(System.Collections.Generic.IEnumerable`1[System.String])
    //System.String Concat(System.String, System.String)
    //System.String Concat(System.String, System.String, System.String)
    //System.String Concat(System.String, System.String, System.String, System.String)
    //System.String Concat(System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char])
    //System.String Concat(System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char])
    //System.String Concat(System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char])
    //System.String Concat(System.String[])
    //System.String Format(System.String, System.Object)
    //System.String Format(System.String, System.Object, System.Object)
    //System.String Format(System.String, System.Object, System.Object, System.Object)
    //System.String Format(System.String, System.Object[])
    //System.String Format(System.IFormatProvider, System.String, System.Object)
    //System.String Format(System.IFormatProvider, System.String, System.Object, System.Object)
    //System.String Format(System.IFormatProvider, System.String, System.Object, System.Object, System.Object)
    //System.String Format(System.IFormatProvider, System.String, System.Object[])
    //System.String Insert(Int32, System.String)
    //System.String Join(Char, System.String[])
    //System.String Join(Char, System.Object[])
    //System.String Join[T](Char, System.Collections.Generic.IEnumerable`1[T])
    //System.String Join(Char, System.String[], Int32, Int32)
    //System.String Join(System.String, System.String[])
    //System.String Join(System.String, System.Object[])
    //System.String Join[T](System.String, System.Collections.Generic.IEnumerable`1[T])
    //System.String Join(System.String, System.Collections.Generic.IEnumerable`1[System.String])
    //System.String Join(System.String, System.String[], Int32, Int32)
    //System.String PadLeft(Int32)
    //System.String PadLeft(Int32, Char)
    //System.String PadRight(Int32)
    //System.String PadRight(Int32, Char)
    //System.String Remove(Int32, Int32)
    //System.String Remove(Int32)
    //System.String Replace(System.String, System.String, Boolean, System.Globalization.CultureInfo)
    //System.String Replace(System.String, System.String, System.StringComparison)
    //System.String Replace(Char, Char)
    //Char get_Chars(Int32)
    //Int32 get_Length()
    //System.String Intern(System.String)
    //System.String IsInterned(System.String)
    //System.String Create[TState](Int32, TState, System.Buffers.SpanAction`2[System.Char, TState])
    //System.ReadOnlySpan`1[System.Char] op_Implicit(System.String)
    //System.Object Clone()
    //System.String Copy(System.String)
    //Void CopyTo(Int32, Char[], Int32, Int32)
    //Char[] ToCharArray()
    //Char[] ToCharArray(Int32, Int32)
    //Boolean IsNullOrEmpty(System.String)
    //Boolean IsNullOrWhiteSpace(System.String)
    //Char & GetPinnableReference()
    //System.String ToString()
    //System.String ToString(System.IFormatProvider)
    //System.CharEnumerator GetEnumerator()
    //System.Text.StringRuneEnumerator EnumerateRunes()
    //System.TypeCode GetTypeCode()
    //Boolean IsNormalized()
    //Boolean IsNormalized(System.Text.NormalizationForm)
    //System.String Normalize()
    //System.String Normalize(System.Text.NormalizationForm)
    //Int32 Compare(System.String, System.String)
    //System.Type GetType()
    #endregion
}

GetMethod方法传入方法名和Type数组作为参数获取类的指定公共成员方法的反射信息

//调用Type类中的GetMethod()方法 传入指定的公共成员方法名 和不同的Type数组参数 Type数组中内容是按顺序是参数类型 这样可以得到对应的公共成员方法
//如果指定的公共成员方法存在重载 用Type数组表示参数类型来分辨不同的重载方法
//要用到MemberInfo类 装载 string类中的所有成员
//MethodInfo类是公共成员方法的反射信息类
//MemberInfo类需要引用命名空间 using System.Reflection;
MethodInfo substring = stringType.GetMethod("Substring", new Type[] { typeof(int), typeof(int) });
//得到的是 public String Substring(int startIndex, int length); 这个截取字符串的重载方法

调用方法反射信息的Invoke,并传入调用对象和object数组作为参数,调用类的指定公共成员方法

//准备好字符例子
string stringHelloWorld = "Hello,World!";
//调用MethodInfo类中的Invoke()方法
//第一个参数 是哪个对象要执行这个成员方法
//第二个参数 传入不同的object数组参数 object数组中的内容是按顺序传入的参数值
//注意1:如果是静态方法 Invoke中的第一个参数传null即可
//注意2:调用MethodInfo类中的Invoke()方法得到的对象还是object类的 所以用object类来装
object substringResult = substring.Invoke(stringHelloWorld, new object[] { 7, 5 });//从第7个位置开始截取五个字符串
Console.WriteLine(substringResult);//orld!
Type类中的其他方法

其他方法包括:

  1. 获取枚举:GetEnumNameGetEnumNames
  2. 获取事件:GetEventGetEvents
  3. 获取接口:GetInterfaceGetInterfaces
  4. 获取属性:GetPropertyGetProperties

等等。


Assembly

Assembly类(程序集类)是用来加载其他程序集的主要类,它是反射功能的基础之一。要使用Assembly类,需要引用命名空间 System.Reflection。主要用来加载其他程序集,加载后才能使用其他程序集中的信息。如果想要使用不是自己程序集中的内容,需要先加载程序集,比如 DLL 文件(库文件)。

加载Assembly程序集的函数
  • Load 方法:返回一个程序集对象,一般用来加载在同一文件下的其他程序集。
    Assembly assembly = Assembly.Load("程序集名称");
    
  • LoadFrom 方法:返回一个程序集对象,一般用来加载不在同一文件下的其他程序集。
    Assembly assembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径");
    
  • LoadFile 方法:返回一个程序集对象,一般用来加载不在同一文件下的其他程序集。
    Assembly assembly = Assembly.LoadFile("要加载的文件的完全限定路径");
    
LoadFrom方法加载一个指定程序集
Assembly asembly =
    Assembly.LoadFrom(@"E:\GameDevelopment\GameDevelopmentLearning\C#\C#进阶\CSharp进阶教程\Lesson18_练习题\bin\Debug\netcoreapp3.1\Lesson18_练习题.dll");
//注意1:传入的路径是dll文件所在的路径 路径的最后要带上dll文件的文件名 也可以加上dll后缀
//注意2:不能直接传入路径 因为\是转义字符 直接输入\不代表\
//取消转义字符有两种方式:
//1.使用\\ 意思是取消转义 这样一个\\就代表一个真正的\
//2.在路径前加一个@
GetTypes方法获得程序集中的所有类的Type并遍历
//用一个Type类数组装载
Type[] types = asembly.GetTypes();
//遍历打印程序集中的所有类的Type
for (int i = 0; i < types.Length; i++)
{
    Console.WriteLine(types[i]);
    //Lesson18_练习题.E_MoveDir
    //Lesson18_练习题.Icon
    //Lesson18_练习题.Program
}
GetType方法传入类名获得程序集中的指定类的Type,通过Type的GetMembers方法可以得到类中的所有公共成员
//调用Assembly类的GetType方法 传入类名 获得程序集中的指定类的Type
Type iconType = asembly.GetType("Lesson18_练习题.Icon");
//调用Type类中的GetMembers()方法 这样可以得到Icon类中的所有公共成员
//只可以得到所有公共成员 私有的得不到 获得其他成员时同理
//要用到MemberInfo类的数组 装载 Icon类中的所有成员
//MemberInfo类是成员的反射信息类
//MemberInfo类需要引用命名空间 using System.Reflection;
MemberInfo[] members = iconType.GetMembers();
//遍历打印Icon类的所有公共成员
for (int i = 0; i < members.Length; i++)
{
    Console.WriteLine(members[i]);
    //Void Move()
    //Void Draw()
    //Void Clear()
    //Void ChangeDir(Lesson18_练习题.E_MoveDir)
    //System.Type GetType()
    //System.String ToString()
    //Boolean Equals(System.Object)
    //Int32 GetHashCode()
    //Void.ctor(Int32, Int32, Lesson18_练习题.E_MoveDir)
    //Lesson18_练习题.E_MoveDir dir
    //Int32 x
    //Int32 y
}
通过反射实例化一个程序集中的对象(icon 对象)并调用其行为
//调用GetType()方法 通过传入枚举名 得到E_MoveDir方向枚举类的Type
//注意:类名必须包含命名空间 不然找不到
Type E_MoveDir_Type = asembly.GetType("Lesson18_练习题.E_MoveDir");

//调用E_MoveDir方向枚举类的Type类中的GetField()方法 传入枚举成员名 可以得到对应的枚举成员
//要用到FieldInfo类装载 E_MoveDir枚举中的枚举成员
//FieldInfo类是公共成员变量的反射信息类
//FieldInfo类需要引用命名空间 using System.Reflection;
FieldInfo rightFieldInfo = E_MoveDir_Type.GetField("Right");
//这样就得到了方向枚举成员right的公共成员变量的反射信息类

//通过反射 实例化一个icon对象
//调用Activator类中的CreateInstance()方法 
//Activator类中的CreateInstance()方法会返回一个object对象 CreateInstance有多个重载
//第一个参数固定传 你想要构造的类的Type类 比如 实例化一个icon对象就传入iconType
//后面的参数是变长参数 根据你想要构造的类 的 构造函数参数 传对应的参数 假如是无参构造函数就可以不传
//这是Icon类的构造函数
//public Icon(int x, int y, E_MoveDir dir)
//{
//    this.x = x;
//    this.y = y;
//    this.dir = dir;
//}
//所以传入两个整形变量和一个方向枚举实例化出icon
//传入实际方向枚举值 要调用方向枚举成员right的公共成员变量的反射信息类FieldInfo中的GetValue()方法
//正常要得到某类的某成员的值时 GetValue()中是要传入某类的实例对象得到的
//但是枚举是特殊的类 没有实例化对象 传null即可
object iconObject = Activator.CreateInstance(iconType, 10, 5, rightFieldInfo.GetValue(null));
//这样就能实例化icon的对象 但是由于icon类不在我们的程序集中 所以没有as成Icon 而是用object去装


//通过反射 得到icon对象中的方法的公共成员方法反射信息类
//调用Type类中的GetMethod()方法 传入指定的公共成员方法名 和不同的Type数组参数 Type数组中内容是按顺序是参数类型 这样可以得到对应的公共成员方法
//如果指定的公共成员方法存在重载 用Type数组表示参数类型来分辨不同的重载方法
//要用到MemberInfo类 装载 string类中的所有成员
//MethodInfo类是公共成员方法的反射信息类
//MemberInfo类需要引用命名空间 using System.Reflection;
MethodInfo move = iconType.GetMethod("Move");
MethodInfo draw = iconType.GetMethod("Draw");
MethodInfo clear = iconType.GetMethod("Clear");

//清空控制台
Console.Clear();
//光标不显示
Console.CursorVisible = false;

//死循环不停的擦除移动绘制图标
while (true)
{
    Thread.Sleep(500);////每次休眠个0.5秒 再擦除移动绘制图标

    //调用MethodInfo类中的Invoke()方法
    //第一个参数 是哪个对象要执行这个成员方法
    //第二个参数 传入不同的object数组参数 object数组中的内容是按顺序传入的参数值
    //注意1:如果是静态方法 Invoke中的第一个参数传null即可
    //注意2:正常来说调用MethodInfo类中的Invoke()方法得到的对象还是object类的 所以正常用object类来装 但是这几个函数都是无返回值的 不用一个object对象来装
    clear.Invoke(iconObject, null);//绘制
    move.Invoke(iconObject, null);//擦除
    draw.Invoke(iconObject, null);//转向
}
创建类库工程
  1. 在解决方案中右键添加新建项目,选择 C# 类库文件,输入类库名,选择类库路径,创建类库工程。

  2. 类库工程中可以创建各种类。类库工程中的类是纯写逻辑用的。

  3. 右键类库工程,点击生成或者重新生成,右键类库工程,在文件资源管理器中打开文件夹,进入 bin 再进入 Debug,就会看到类库工程的 DLL 文件。


  4. 这样就可以用反射的知识,调用类库工程中的逻辑内容。

Activator

Activator类(用于快速实例化对象的类)是用于将Type对象快速实例化为对象的类。通过Activator类,可以方便地实例化对象,但需要先获取到对应的Type。

//首先得到Test类的Type 这里就不新建了 用上面的
testType = typeof(Test);
获取类的指定公共构造函数

调用Activator类中的CreateInstance()方法会返回一个object对象。CreateInstance有多个重载。

第一个参数固定传你想要构造的类的Type类,比如Test类的Type。

后面的参数是变长参数,根据你想要构造的类的构造函数参数传对应的参数。假如是无参构造函数就可以不传。

比如:

  • Test类的Test()无参构造函数就传Activator.CreateInstance(testType)
  • Test类的Test(int i)有一个整型参数的构造函数就传Activator.CreateInstance(testType, 99)
  • Test类的Test(int i, string str):this(i)有一个整型参数和一个字符串型参数的构造函数就传Activator.CreateInstance(testType, 55, "111222")

注意:返回的对象的实际类是传入的你想要构造的类,但是还是以object对象返回的,可以用as把他转成你想要构造的类。

调用无参构造快速实例化对象的类
//无参构造函数
Test testObj = Activator.CreateInstance(testType) as Test;
Console.WriteLine(testObj.str);//123
调用有参构造快速实例化对象的类
//有一个整型参数的构造函数
testObj = Activator.CreateInstance(testType, 99) as Test;
Console.WriteLine(testObj.j);//0

//有一个整型参数和一个字符串型参数的构造函数
testObj = Activator.CreateInstance(testType, 55, "111222") as Test;
Console.WriteLine(testObj.j);//0
Console.WriteLine(testObj.str);//111222
注意

如果想要构造的类中没有和传入 CreateInstance 中后面的参数匹配的构造函数,编译时不会报错,但运行时会报错。

//testObj = Activator.CreateInstance(testType,"111222") as Test;//运行时会报错 Test类中没有只有一个字符串型参数的构造函数

总结

  • 反射
    • 在程序运行时,通过反射可以得到其他程序集或者自己的程序集代码的各种信息,如类、函数、变量、对象等等,实例化他们,执行他们,操作他们。
  • 关键类
    • Type
    • Assembly
    • Activator
  • 反射对于我们的意义
    • 在初中级阶段基本不会使用反射,所以目前对于大家来说,了解反射可以做什么就行,很长时间内都不会用到反射相关知识点。
  • 为什么要学反射
    • 为了之后学习Unity引擎的基本工作原理做铺垫,Unity引起的基本工作机制就是建立在反射的基础上。

21.2 知识点代码

using System;
using System.Reflection;
using System.Threading;

namespace Lesson20_反射
{
    #region 知识点回顾
    //编译器是一种翻译程序
    //它用于将源语言程序翻译为目标语言程序

    //源语言程序:某种程序设计语言写成的,比如C#、C、C++、Java等语言写的程序
    //目标语言程序:二进制数表示的伪机器代码写的程序
    #endregion

    #region 知识点一 程序集
    //程序集是经由编译器编译得到的,供进一步编译执行的那个中间产物
    //在WINDOWS系统中,它一般表现为后缀为·dll(库文件)或者是·exe(可执行文件)的格式

    //说人话:
    //程序集就是我们写的一个代码集合,我们现在写的所有代码
    //最终都会被编译器翻译为一个程序集供别人使用
    //比如一个代码库文件(dll)或者一个可执行文件(exe)
    #endregion

    #region 知识点二 元数据
    //元数据就是用来描述数据的数据
    //这个概念不仅仅用于程序上,在别的领域也有元数据

    //说人话:
    //程序中的类,类中的函数、变量等等信息就是 程序的 元数据
    //有关程序以及类型的数据被称为 元数据,它们保存在程序集中
    #endregion

    #region 知识点三 反射的概念
    //程序正在运行时,可以查看其它程序集或者自身的元数据。
    //一个运行的程序查看本身或者其它程序的元数据的行为就叫做反射

    //说人话:
    //在程序运行时,通过反射可以得到其它程序集或者自己程序集代码的各种信息
    //类,函数,变量,对象等等,实例化它们,执行它们,操作它们
    #endregion

    #region 知识点四 反射的作用
    //因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性
    //1.程序运行时得到所有元数据,包括元数据的特性
    //2.程序运行时,实例化对象,操作对象
    //3.程序运行时创建新对象,用这些对象执行任务
    #endregion

    //class语句块内 namespace语句块外

    #region 知识点五 反射的语法相关

    //测试类
    class Test
    {
        private int i = 1;
        public int j = 0;
        public string str = "123";
        public Test()
        {

        }

        public Test(int i)
        {
            this.i = i;
        }

        public Test(int i, string str) : this(i)
        {
            this.str = str;

        }

        public void Speak()
        {
            Console.WriteLine(i);
        }
    }

    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("反射");

            //主函数内

            #region 知识点五 反射的语法相关

            #region Type

            Console.WriteLine("Type");

            //Type类(类的信息类)
            //它是反射功能的基础
            //它是访问元数据的主要方式 
            //使用 Type 的成员获取有关类型声明的信息
            //有关类型的成员(如构造函数、方法、字段、属性和类的事件)

            #region 获取Type对象

            Console.WriteLine("获取Type对象");

            //1.对象调用GetType()方法 万物之父object中的方法 传入对象 可以获取对象的Type
            int a = 42;
            Type type1 = a.GetType();
            Console.WriteLine(type1);//System.Int32

            //2.通过typeof关键字传入类名 也可以得到对象的Type
            Type type2 = typeof(int);
            Console.WriteLine(type2);//System.Int32

            //3.Type.GetType()方法传入类名 万物之父object中的方法 通过传入类的名字 也可以获取类型
            //注意:类名必须包含命名空间 不然找不到
            Type type3 = Type.GetType("System.Int32");
            Console.WriteLine(type3);//System.Int32
            Type type4 = Type.GetType("Int32");
            Console.WriteLine(type4);//type4没有包含命名空间 找不到

            //注意:type1 type2 type3 指向堆空间的内存地址是一样(指向的都是同一个房间)

            #endregion

            #region 得到类的程序集信息

            Console.WriteLine("得到类的程序集信息");

            //Type类中的Assembly属性 可以返回类型所在程序集信息
            Console.WriteLine(type1.Assembly);//System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
            Console.WriteLine(type2.Assembly);//System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
            Console.WriteLine(type3.Assembly);//System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e

            #endregion

            #region 获取类中的所有公共成员的反射信息

            Console.WriteLine("获取类中的所有公共成员");

            //首先得到Test类的Type
            Type testType = typeof(Test);

            //获取类中的所有公共成员
            //调用Type类中的GetMembers()方法 这样可以得到Test类中的所有公共成员
            //只可以得到所有公共成员 私有的得不到 获得其他成员时同理
            //要用到MemberInfo类的数组 装载 Test类中的所有成员
            //MemberInfo类是成员的反射信息类
            //MemberInfo类需要引用命名空间 using System.Reflection;
            MemberInfo[] infos = testType.GetMembers();
            //遍历打印Test类的所有公共成员
            for (int i = 0; i < infos.Length; i++)
            {
                Console.WriteLine(infos[i]);
                //Void Speak()
                //System.Type GetType()
                //System.String ToString()
                //Boolean Equals(System.Object)
                //Int32 GetHashCode()
                //Void.ctor()//这是无参构造函数
                //Void.ctor(Int32)//这是有参构造函数
                //Void.ctor(Int32, System.String)//这是有参构造函数
                //Int32 j
                //System.String stringHelloWorld
            }

            #endregion

            #region 获取类的公共构造函数的反射信息并调用

            Console.WriteLine("获取类的公共构造函数并调用");

            //1.获取类的所有公共构造函数
            //调用Type类中的GetConstructors()方法 这样可以得到Test类中的所有构造函数
            //要用到ConstructorInfo类的数组 装载 Test类中的所有构造函数
            //ConstructorInfo类是构造函数的反射信息类
            //ConstructorInfo类需要引用命名空间 using System.Reflection;
            ConstructorInfo[] ctors = testType.GetConstructors();
            //遍历打印Test类的所有构造函数
            for (int i = 0; i < ctors.Length; i++)
            {
                Console.WriteLine(ctors[i]);
                //Void.ctor()
                //Void.ctor(Int32)
                //Void.ctor(Int32, System.String)
            }

            //2.获取类的指定公共构造函数并调用

            //获取类的指定公共构造函数
            //调用Type类中的GetConstructor()方法 传入不同的Type数组参数 Type数组中内容是按顺序是参数类型 这样可以得到Test类中的不同的构造函数
            //要用到ConstructorInfo类 装载 Test类中的所有构造函数
            //ConstructorInfo类是构造函数的反射信息类
            //ConstructorInfo类需要引用命名空间 using System.Reflection;

            //调用指定的公共构造函数
            //调用ConstructorInfo类中的Invoke()方法 传入不同的object数组参数 object数组中的内容是按顺序传入的参数值 这样可以执行Test类中的不同的构造函数
            //注意:调用ConstructorInfo类中的Invoke()方法得到的对象还是object类的 要把他as成Test类才能得到Test类的对象

            //获取类的公共无参构造函数并调用
            //调用Type类中的GetConstructor()方法 传入new Type[0]
            //传入长度为0的空Type数组 意味着这个构造函数没有参数 这样可以得到Type中的公共无参构造函数
            ConstructorInfo testNoParameterConstructor = testType.GetConstructor(new Type[0]);
            //执行无参构造 调用ConstructorInfo类中的Invoke()方法 传入object数组
            //正常来说要传入object数组 充当按顺序传入的参数值 但是由于是无参构造 没有参数 所以传null代表传入空的object数组
            Test test1 = testNoParameterConstructor.Invoke(null) as Test;//执行无参构造
            Console.WriteLine("test1.j:" + test1.j);//test1.j:0

            //获取类的公共有参构造函数并调用

            //调用Type类中的GetConstructor()方法 传入new Type[] { typeof(int) }
            //代表着传入长度为1 有一个整形类型变量的数组 可以得到Type中的有一个int类型参数的有参构造
            ConstructorInfo testHaveIntParameterConstructor = testType.GetConstructor(new Type[] { typeof(int) });
            //执行有参构造  调用ConstructorInfo类中的Invoke()方法 传入new object[] { 2 }这个object数组 代表传入有一个int类型变量2的object数组
            Test test2 = testHaveIntParameterConstructor.Invoke(new object[] { 2 }) as Test;//这样 通过构造函数 i被初始化为2了
            Console.WriteLine("test2.stringHelloWorld:" + test2.str);//test2.stringHelloWorld:123

            //调用Type类中的GetConstructor()方法 传入new Type[] { typeof(int), typeof(string) }
            //代表着传入长度为2 有一个整形类型变量和一个string类型变量的数组 可以得到Type中的有一个int类型参数和一个string类型参数的有参构造
            ConstructorInfo testHaveIntAndStringParameterConstructor = testType.GetConstructor(new Type[] { typeof(int), typeof(string) });
            //执行有参构造 调用ConstructorInfo类中的Invoke()方法 传入new object[] { 4, "444444" }这个object数组 代表传入有一个int类型变量4和一个string类型变量"444444"的object数组
            Test test3 = testHaveIntAndStringParameterConstructor.Invoke(new object[] { 4, "444444" }) as Test;//这样 通过构造函数 i被初始化为4了 str被初始化为444444
            Console.WriteLine("test3.stringHelloWorld:" + test3.str);//test3.stringHelloWorld:444444

            #endregion

            #region 获取类的公共成员变量的反射信息并修改

            Console.WriteLine("获取类的公共成员变量并修改");

            //1.获取类的所有公共成员变量
            //调用Type类中的GetFields()方法 可以得到Test类中的所有公共成员变量
            //要用到FieldInfo类的数组 装载 Test类中的所有公共成员变量
            //FieldInfo类是公共成员变量的反射信息类
            //FieldInfo类需要引用命名空间 using System.Reflection;
            FieldInfo[] fieldInfos = testType.GetFields();
            //遍历打印Test类的所有公共成员变量
            for (int i = 0; i < fieldInfos.Length; i++)
            {
                Console.WriteLine(fieldInfos[i]);
                //Int32 j
                //System.String stringHelloWorld
            }

            //2.获取类的指定公共成员变量
            //调用Type类中的GetField()方法 传入公共成员变量名 可以得到对应的公共成员变量
            //要用到FieldInfo类装载 Test类中的公共成员变量
            //FieldInfo类是公共成员变量的反射信息类
            //FieldInfo类需要引用命名空间 using System.Reflection;
            FieldInfo fieldInfoJ = testType.GetField("j");
            Console.WriteLine(fieldInfoJ);//Int32 j

            //3.通过公共成员变量的反射信息 获取并修改指定公共成员变量的值

            //new出一个测试类例子test4
            Test test4 = new Test();
            test4.j = 99;
            test4.str = "2222";

            //通过公共成员变量的反射信息 获取对象的某个变量的值
            //调用FieldInfo类中的GetValue()方法 传入Test类的对象
            //注意:要先得到用FieldInfo类装的指定名称的公共成员变量的反射信息
            Console.WriteLine(fieldInfoJ.GetValue(test4));//99

            //通过公共成员变量的反射信息 修改指定对象的某个变量的值
            //调用FieldInfo类中的SetValue()方法 传入Test类的对象和要设置的值
            //注意:要先得到用FieldInfo类装的指定名称的公共成员变量的反射信息
            fieldInfoJ.SetValue(test4, 100);//修改指定对象的某个变量的值
            Console.WriteLine(fieldInfoJ.GetValue(test4));//100

            #endregion

            #region 获取类的公共成员方法的反射信息并调用

            Console.WriteLine("获取类的公共成员方法并调用");

            //首先得到string类的Type
            Type stringType = typeof(string);

            //1.获取类的所有公共成员方法
            //调用Type类中的GetMethods()方法 这样可以得到string类中的所有成员方法
            //要用到MemberInfo类的数组 装载 string类中的所有成员
            //MethodInfo类是公共成员方法的反射信息类
            //MemberInfo类需要引用命名空间 using System.Reflection;
            MethodInfo[] methods = stringType.GetMethods();
            //遍历打印string类的所有公共成员方法
            for (int i = 0; i < methods.Length; i++)
            {
                Console.WriteLine(methods[i]);

                #region string类的所有公共成员方法
                //System.String Replace(System.String, System.String)
                //System.String[] Split(Char, System.StringSplitOptions)
                //System.String[] Split(Char, Int32, System.StringSplitOptions)
                //System.String[] Split(Char[])
                //System.String[] Split(Char[], Int32)
                //System.String[] Split(Char[], System.StringSplitOptions)
                //System.String[] Split(Char[], Int32, System.StringSplitOptions)
                //System.String[] Split(System.String, System.StringSplitOptions)
                //System.String[] Split(System.String, Int32, System.StringSplitOptions)
                //System.String[] Split(System.String[], System.StringSplitOptions)
                //System.String[] Split(System.String[], Int32, System.StringSplitOptions)
                //System.String Substring(Int32)
                //System.String Substring(Int32, Int32)
                //System.String ToLower()
                //System.String ToLower(System.Globalization.CultureInfo)
                //System.String ToLowerInvariant()
                //System.String ToUpper()
                //System.String ToUpper(System.Globalization.CultureInfo)
                //System.String ToUpperInvariant()
                //System.String Trim()
                //System.String Trim(Char)
                //System.String Trim(Char[])
                //System.String TrimStart()
                //System.String TrimStart(Char)
                //System.String TrimStart(Char[])
                //System.String TrimEnd()
                //System.String TrimEnd(Char)
                //System.String TrimEnd(Char[])
                //Boolean Contains(System.String)
                //Boolean Contains(System.String, System.StringComparison)
                //Boolean Contains(Char)
                //Boolean Contains(Char, System.StringComparison)
                //Int32 IndexOf(Char)
                //Int32 IndexOf(Char, Int32)
                //Int32 IndexOf(Char, System.StringComparison)
                //Int32 IndexOf(Char, Int32, Int32)
                //Int32 IndexOfAny(Char[])
                //Int32 IndexOfAny(Char[], Int32)
                //Int32 IndexOfAny(Char[], Int32, Int32)
                //Int32 IndexOf(System.String)
                //Int32 IndexOf(System.String, Int32)
                //Int32 IndexOf(System.String, Int32, Int32)
                //Int32 IndexOf(System.String, System.StringComparison)
                //Int32 IndexOf(System.String, Int32, System.StringComparison)
                //Int32 IndexOf(System.String, Int32, Int32, System.StringComparison)
                //Int32 LastIndexOf(Char)
                //Int32 LastIndexOf(Char, Int32)
                //Int32 LastIndexOf(Char, Int32, Int32)
                //Int32 LastIndexOfAny(Char[])
                //Int32 LastIndexOfAny(Char[], Int32)
                //Int32 LastIndexOfAny(Char[], Int32, Int32)
                //Int32 LastIndexOf(System.String)
                //Int32 LastIndexOf(System.String, Int32)
                //Int32 LastIndexOf(System.String, Int32, Int32)
                //Int32 LastIndexOf(System.String, System.StringComparison)
                //Int32 LastIndexOf(System.String, Int32, System.StringComparison)
                //Int32 LastIndexOf(System.String, Int32, Int32, System.StringComparison)
                //Int32 Compare(System.String, System.String, Boolean)
                //Int32 Compare(System.String, System.String, System.StringComparison)
                //Int32 Compare(System.String, System.String, System.Globalization.CultureInfo, System.Globalization.CompareOptions)
                //Int32 Compare(System.String, System.String, Boolean, System.Globalization.CultureInfo)
                //Int32 Compare(System.String, Int32, System.String, Int32, Int32)
                //Int32 Compare(System.String, Int32, System.String, Int32, Int32, Boolean)
                //Int32 Compare(System.String, Int32, System.String, Int32, Int32, Boolean, System.Globalization.CultureInfo)
                //Int32 Compare(System.String, Int32, System.String, Int32, Int32, System.Globalization.CultureInfo, System.Globalization.CompareOptions)
                //Int32 Compare(System.String, Int32, System.String, Int32, Int32, System.StringComparison)
                //Int32 CompareOrdinal(System.String, System.String)
                //Int32 CompareOrdinal(System.String, Int32, System.String, Int32, Int32)
                //Int32 CompareTo(System.Object)
                //Int32 CompareTo(System.String)
                //Boolean EndsWith(System.String)
                //Boolean EndsWith(System.String, System.StringComparison)
                //Boolean EndsWith(System.String, Boolean, System.Globalization.CultureInfo)
                //Boolean EndsWith(Char)
                //Boolean Equals(System.Object)
                //Boolean Equals(System.String)
                //Boolean Equals(System.String, System.StringComparison)
                //Boolean Equals(System.String, System.String)
                //Boolean Equals(System.String, System.String, System.StringComparison)
                //Boolean op_Equality(System.String, System.String)
                //Boolean op_Inequality(System.String, System.String)
                //Int32 GetHashCode()
                //Int32 GetHashCode(System.StringComparison)
                //Int32 GetHashCode(System.ReadOnlySpan`1[System.Char])
                //Int32 GetHashCode(System.ReadOnlySpan`1[System.Char], System.StringComparison)
                //Boolean StartsWith(System.String)
                //Boolean StartsWith(System.String, System.StringComparison)
                //Boolean StartsWith(System.String, Boolean, System.Globalization.CultureInfo)
                //Boolean StartsWith(Char)
                //System.String Concat(System.Object)
                //System.String Concat(System.Object, System.Object)
                //System.String Concat(System.Object, System.Object, System.Object)
                //System.String Concat(System.Object[])
                //System.String Concat[T](System.Collections.Generic.IEnumerable`1[T])
                //System.String Concat(System.Collections.Generic.IEnumerable`1[System.String])
                //System.String Concat(System.String, System.String)
                //System.String Concat(System.String, System.String, System.String)
                //System.String Concat(System.String, System.String, System.String, System.String)
                //System.String Concat(System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char])
                //System.String Concat(System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char])
                //System.String Concat(System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char], System.ReadOnlySpan`1[System.Char])
                //System.String Concat(System.String[])
                //System.String Format(System.String, System.Object)
                //System.String Format(System.String, System.Object, System.Object)
                //System.String Format(System.String, System.Object, System.Object, System.Object)
                //System.String Format(System.String, System.Object[])
                //System.String Format(System.IFormatProvider, System.String, System.Object)
                //System.String Format(System.IFormatProvider, System.String, System.Object, System.Object)
                //System.String Format(System.IFormatProvider, System.String, System.Object, System.Object, System.Object)
                //System.String Format(System.IFormatProvider, System.String, System.Object[])
                //System.String Insert(Int32, System.String)
                //System.String Join(Char, System.String[])
                //System.String Join(Char, System.Object[])
                //System.String Join[T](Char, System.Collections.Generic.IEnumerable`1[T])
                //System.String Join(Char, System.String[], Int32, Int32)
                //System.String Join(System.String, System.String[])
                //System.String Join(System.String, System.Object[])
                //System.String Join[T](System.String, System.Collections.Generic.IEnumerable`1[T])
                //System.String Join(System.String, System.Collections.Generic.IEnumerable`1[System.String])
                //System.String Join(System.String, System.String[], Int32, Int32)
                //System.String PadLeft(Int32)
                //System.String PadLeft(Int32, Char)
                //System.String PadRight(Int32)
                //System.String PadRight(Int32, Char)
                //System.String Remove(Int32, Int32)
                //System.String Remove(Int32)
                //System.String Replace(System.String, System.String, Boolean, System.Globalization.CultureInfo)
                //System.String Replace(System.String, System.String, System.StringComparison)
                //System.String Replace(Char, Char)
                //Char get_Chars(Int32)
                //Int32 get_Length()
                //System.String Intern(System.String)
                //System.String IsInterned(System.String)
                //System.String Create[TState](Int32, TState, System.Buffers.SpanAction`2[System.Char, TState])
                //System.ReadOnlySpan`1[System.Char] op_Implicit(System.String)
                //System.Object Clone()
                //System.String Copy(System.String)
                //Void CopyTo(Int32, Char[], Int32, Int32)
                //Char[] ToCharArray()
                //Char[] ToCharArray(Int32, Int32)
                //Boolean IsNullOrEmpty(System.String)
                //Boolean IsNullOrWhiteSpace(System.String)
                //Char & GetPinnableReference()
                //System.String ToString()
                //System.String ToString(System.IFormatProvider)
                //System.CharEnumerator GetEnumerator()
                //System.Text.StringRuneEnumerator EnumerateRunes()
                //System.TypeCode GetTypeCode()
                //Boolean IsNormalized()
                //Boolean IsNormalized(System.Text.NormalizationForm)
                //System.String Normalize()
                //System.String Normalize(System.Text.NormalizationForm)
                //Int32 Compare(System.String, System.String)
                //System.Type GetType()
                #endregion
            }


            //2.获取类的指定公共成员方法
            //调用Type类中的GetMethod()方法 传入指定的公共成员方法名 和不同的Type数组参数 Type数组中内容是按顺序是参数类型 这样可以得到对应的公共成员方法
            //如果指定的公共成员方法存在重载 用Type数组表示参数类型来分辨不同的重载方法
            //要用到MemberInfo类 装载 string类中的所有成员
            //MethodInfo类是公共成员方法的反射信息类
            //MemberInfo类需要引用命名空间 using System.Reflection;
            MethodInfo substring = stringType.GetMethod("Substring", new Type[] { typeof(int), typeof(int) });
            //得到的是 public String Substring(int startIndex, int length); 这个截取字符串的重载方法


            //3.调用类的指定公共成员方法
            //准备好字符例子
            string stringHelloWorld = "Hello,World!";
            //调用MethodInfo类中的Invoke()方法
            //第一个参数 是哪个对象要执行这个成员方法
            //第二个参数 传入不同的object数组参数 object数组中的内容是按顺序传入的参数值
            //注意1:如果是静态方法 Invoke中的第一个参数传null即可
            //注意2:调用MethodInfo类中的Invoke()方法得到的对象还是object类的 所以用object类来装
            object substringResult = substring.Invoke(stringHelloWorld, new object[] { 7, 5 });//从第7个位置开始截取五个字符串
            Console.WriteLine(substringResult);//orld!

            #endregion

            #region Type类中的其它方法

            //Type类中的其它方法可以仿照之前的例子 大同小异

            //得枚举
            //GetEnumName
            //GetEnumNames

            //得事件
            //GetEvent
            //GetEvents

            //得接口
            //GetInterface
            //GetInterfaces

            //得属性
            //GetProperty
            //GetPropertys

            //等等
            #endregion

            #endregion

            #region Assembly

            Console.WriteLine("Assembly");

            //Assembly类(程序集类)
            //Assembly类需要引用命名空间 using System.Reflection;
            //主要用来加载其它程序集,加载后
            //才能用Type来使用其它程序集中的信息
            //如果想要使用不是自己程序集中的内容 需要先加载程序集
            //比如 dll文件(库文件) 
            //简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类

            //Assembly类的三种加载程序集的函数
            //Assembly类的Load方法 返回一个程序集对象 一般用来加载在同一文件下的其它程序集
            //Assembly asembly = Assembly.Load("程序集名称");
            //Assembly类的LoadFrom方法 返回一个程序集对象 一般用来加载不在同一文件下的其它程序集
            //Assembly asembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径");
            //Assembly类的LoadFile方法 返回一个程序集对象 一般用来加载不在同一文件下的其它程序集
            //Assembly asembly = Assembly.LoadFile("要加载的文件的完全限定路径");



            //调用Assembly类的LoadFrom方法 加载一个指定程序集
            Assembly asembly =
                Assembly.LoadFrom(@"E:\GameDevelopment\GameDevelopmentLearning\C#\C#进阶\CSharp进阶教程\Lesson18_练习题\bin\Debug\netcoreapp3.1\Lesson18_练习题.dll");
            //注意1:传入的路径是dll文件所在的路径 路径的最后要带上dll文件的文件名 也可以加上dll后缀
            //注意2:不能直接传入路径 因为\是转义字符 直接输入\不代表\
            //取消转义字符有两种方式:
            //1.使用\\ 意思是取消转义 这样一个\\就代表一个真正的\
            //2.在路径前加一个@



            //调用Assembly类的GetTypes方法 获得程序集中的所有类的Type
            //用一个Type类数组装载
            Type[] types = asembly.GetTypes();
            //遍历打印程序集中的所有类的Type
            for (int i = 0; i < types.Length; i++)
            {
                Console.WriteLine(types[i]);
                //Lesson18_练习题.E_MoveDir
                //Lesson18_练习题.Icon
                //Lesson18_练习题.Program
            }


            //调用Assembly类的GetType方法 传入类名 获得程序集中的指定类的Type
            Type iconType = asembly.GetType("Lesson18_练习题.Icon");
            //调用Type类中的GetMembers()方法 这样可以得到Icon类中的所有公共成员
            //只可以得到所有公共成员 私有的得不到 获得其他成员时同理
            //要用到MemberInfo类的数组 装载 Icon类中的所有成员
            //MemberInfo类是成员的反射信息类
            //MemberInfo类需要引用命名空间 using System.Reflection;
            MemberInfo[] members = iconType.GetMembers();
            //遍历打印Icon类的所有公共成员
            for (int i = 0; i < members.Length; i++)
            {
                Console.WriteLine(members[i]);
                //Void Move()
                //Void Draw()
                //Void Clear()
                //Void ChangeDir(Lesson18_练习题.E_MoveDir)
                //System.Type GetType()
                //System.String ToString()
                //Boolean Equals(System.Object)
                //Int32 GetHashCode()
                //Void.ctor(Int32, Int32, Lesson18_练习题.E_MoveDir)
                //Lesson18_练习题.E_MoveDir dir
                //Int32 x
                //Int32 y
            }



            //通过反射 实例化一个程序集中的对象(icon对象)并调用其行为

            //调用GetType()方法 通过传入枚举名 得到E_MoveDir方向枚举类的Type
            //注意:类名必须包含命名空间 不然找不到
            Type E_MoveDir_Type = asembly.GetType("Lesson18_练习题.E_MoveDir");

            //调用E_MoveDir方向枚举类的Type类中的GetField()方法 传入枚举成员名 可以得到对应的枚举成员
            //要用到FieldInfo类装载 E_MoveDir枚举中的枚举成员
            //FieldInfo类是公共成员变量的反射信息类
            //FieldInfo类需要引用命名空间 using System.Reflection;
            FieldInfo rightFieldInfo = E_MoveDir_Type.GetField("Right");
            //这样就得到了方向枚举成员right的公共成员变量的反射信息类

            //通过反射 实例化一个icon对象
            //调用Activator类中的CreateInstance()方法 
            //Activator类中的CreateInstance()方法会返回一个object对象 CreateInstance有多个重载
            //第一个参数固定传 你想要构造的类的Type类 比如 实例化一个icon对象就传入iconType
            //后面的参数是变长参数 根据你想要构造的类 的 构造函数参数 传对应的参数 假如是无参构造函数就可以不传
            //这是Icon类的构造函数
            //public Icon(int x, int y, E_MoveDir dir)
            //{
            //    this.x = x;
            //    this.y = y;
            //    this.dir = dir;
            //}
            //所以传入两个整形变量和一个方向枚举实例化出icon
            //传入实际方向枚举值 要调用方向枚举成员right的公共成员变量的反射信息类FieldInfo中的GetValue()方法
            //正常要得到某类的某成员的值时 GetValue()中是要传入某类的实例对象得到的
            //但是枚举是特殊的类 没有实例化对象 传null即可
            object iconObject = Activator.CreateInstance(iconType, 10, 5, rightFieldInfo.GetValue(null));
            //这样就能实例化icon的对象 但是由于icon类不在我们的程序集中 所以没有as成Icon 而是用object去装


            //通过反射 得到icon对象中的方法的公共成员方法反射信息类
            //调用Type类中的GetMethod()方法 传入指定的公共成员方法名 和不同的Type数组参数 Type数组中内容是按顺序是参数类型 这样可以得到对应的公共成员方法
            //如果指定的公共成员方法存在重载 用Type数组表示参数类型来分辨不同的重载方法
            //要用到MemberInfo类 装载 string类中的所有成员
            //MethodInfo类是公共成员方法的反射信息类
            //MemberInfo类需要引用命名空间 using System.Reflection;
            MethodInfo move = iconType.GetMethod("Move");
            MethodInfo draw = iconType.GetMethod("Draw");
            MethodInfo clear = iconType.GetMethod("Clear");

            //清空控制台
            Console.Clear();
            //光标不显示
            Console.CursorVisible = false;

            //死循环不停的擦除移动绘制图标
            while (true)
            {
                Thread.Sleep(500);////每次休眠个0.5秒 再擦除移动绘制图标

                //调用MethodInfo类中的Invoke()方法
                //第一个参数 是哪个对象要执行这个成员方法
                //第二个参数 传入不同的object数组参数 object数组中的内容是按顺序传入的参数值
                //注意1:如果是静态方法 Invoke中的第一个参数传null即可
                //注意2:正常来说调用MethodInfo类中的Invoke()方法得到的对象还是object类的 所以正常用object类来装 但是这几个函数都是无返回值的 不用一个object对象来装
                clear.Invoke(iconObject, null);//绘制
                move.Invoke(iconObject, null);//擦除
                draw.Invoke(iconObject, null);//转向
            }

            //创建类库工程
            //在解决方案中 右键 添加 新建项目 选择C#类库文件 输入类库名 选择类库路径 创建类库工程
            //类库工程里可以创建各种类 类库工程中的类是纯写逻辑用的
            //右键类库工程 点击生成或者重新生成 右键类库工程 在文件资源管理器中打开文件夹 进入bin再进入Debug 就会看到类库工程的dll文件
            //这样就可以用反射的知识 调用类库工程中的逻辑内容

            #endregion

            #region Activator

            Console.WriteLine("Activator");

            //Activator类(用于快速实例化对象的类)
            //用于将Type对象快捷实例化为对象
            //要先得到Type
            //然后就快速实例化一个对象

            //首先得到Test类的Type 这里就不新建了 用上面的
            testType = typeof(Test);

            //获取类的指定公共构造函数
            //调用Activator类中的CreateInstance()方法 会返回一个object对象 CreateInstance有多个重载
            //第一个参数固定传 你想要构造的类的Type类 比如Test类的Type
            //后面的参数是变长参数 根据你想要构造的类 的 构造函数参数 传对应的参数 假如是无参构造函数就可以不传
            //比如:
            //Test类的Test()无参构造函数就传Activator.CreateInstance(testType)
            //Test类的Test(int i)有一个整型参数的构造函数就传Activator.CreateInstance(testType, 99)
            //Test类的Test( int i, string str ):this(i)有一个整型参数和一个字符串型参数的构造函数就传Activator.CreateInstance(testType, 55, "111222")
            //注意:返回的对象的实际类 是传入的你想要构造的类 但是还是以object对象返回的 可以用as把他转成你想要构造的类

            //调用无参构造快速实例化对象的类
            //无参构造函数
            Test testObj = Activator.CreateInstance(testType) as Test;
            Console.WriteLine(testObj.str);//123

            //调用有参构造快速实例化对象的类
            //有一个整型参数的构造函数
            testObj = Activator.CreateInstance(testType, 99) as Test;
            Console.WriteLine(testObj.j);//0
            //有一个整型参数和一个字符串型参数的构造函数
            testObj = Activator.CreateInstance(testType, 55, "111222") as Test;
            Console.WriteLine(testObj.j);//0
            Console.WriteLine(testObj.str);//111222

            //注意:假如 你想要构造的类中 没有和你传入CreateInstance中后面的参数匹配的构造函数 编译时不会报错 运行时会报错
            //testObj = Activator.CreateInstance(testType,"111222") as Test;//运行时会报错 Test类中没有只有一个字符串型参数的构造函数
            #endregion

            #endregion
        }
    }

    //总结

    //反射
    //在程序运行时,通过反射可以得到其他程序集或者自己的程序集代码的各种信息
    //类、函数、变量、对象等等,实例化他们,执行他们,操作他们

    //关键类
    //Type
    //Assembly
    //Activator

    //反射对于我们的意义
    //在初中级阶段 基本不会使用反射
    //所以目前对于大家来说,了解反射可以做什么就行
    //很长时间内都不会用到反射相关知识点

    //为什么要学反射
    //为了之后学习Unity引擎的基本工作原理做铺垫
    //Unity引起的基本工作机制 就是建立在反射的基础上


}

21.3 练习题

新建一个类库工程,有一个Player类,有姓名,血量,攻击力,防御力,位置等信息,有一个无参构造函数,再新建一个控制台工程,通过反射的形式使用类库工程生成的dll程序集,实例化一个Player对象

回忆创建类库工程步骤

  1. 在解决方案中右键添加新建项目,选择C#类库文件,输入类库名,选择类库路径,创建类库工程。类库工程里可以创建各种类,类库工程中的类是纯写逻辑用的,我们按照要求写入Player类和相关代码。

using System;

namespace MrTao
{
    class MyCustomAttribute : Attribute
    {

    }

    // 位置
    struct Position
    {
        public int x;
        public int y;
        public Position(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }

    // 玩家
    class Player
    {
        [MyCustom()]
        public string name;

        public int hp;
        public int atk;
        public int def;
        public Position pos;
    }
}

  1. 右键类库工程,点击生成或者重新生成;右键类库工程,在文件资源管理器中打开文件夹,进入bin再进入Debug,就会看到类库工程的dll文件。


调用Assembly类的LoadFrom方法加载一个指定程序集

Assembly assembly = Assembly.LoadFrom(@"E:\GameDevelopment\GameDevelopmentLearning\C#\C#进阶\CSharp进阶教程\测试\bin\Debug\测试");
//注意1:传入的路径是dll文件所在的路径 路径的最后要带上dll文件的文件名 也可以加上dll后缀
//注意2:不能直接传入路径 因为\是转义字符 直接输入\不代表\
//取消转义字符有两种方式:
//1.使用\\ 意思是取消转义 这样一个\\就代表一个真正的\
//2.在路径前加一个@

调用Assembly类的GetTypes方法获得程序集中的所有类的Type

// 用一个Type类数组装载
Type[] types = assembly.GetTypes();

遍历打印程序集中的所有类的Type

for (int i = 0; i < types.Length; i++)
{
    Console.WriteLine(types[i]);
    // MrTao.MyCustomAttribute
    // MrTao.Position
    // MrTao.Player
}

通过反射实例化一个程序集中的对象(player对象)

  1. 调用GetType()方法,通过传入类名,得到MrTao.Player类的Type。
// 注意:类名必须包含命名空间,不然找不到
Type playerType = assembly.GetType("MrTao.Player");
  1. 调用Activator类中的CreateInstance()方法。
// Activator类中的CreateInstance()方法会返回一个object对象,CreateInstance有多个重载
// 第一个参数固定传入你想要构造的类的Type类,比如,实例化一个icon对象就传入iconType
// 后面的参数是变长参数,根据你想要构造的类的构造函数参数传对应的参数,假如是无参构造函数就可以不传
// Player类只有无参构造函数,所以可以不传
object playerObject = Activator.CreateInstance(playerType);
// 这样就能实例化player的对象,但是由于Player类不在我们的程序集中,所以没有as成Player,而是用object去装

打印playerObject

Console.WriteLine(playerObject); // MrTao.Player

21.4 练习题代码

using System;
using System.Reflection;

namespace Lesson20_练习题
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("反射练习题");
            #region 练习题 一
            //新建一个类库工程
            //有一个Player类,有姓名,血量,攻击力,防御力,位置等信息
            //有一个无参构造函数
            //再新建一个控制台工程
            //通过反射的形式使用类库工程生成的dll程序集
            //实例化一个Player对象
            //加载类库生成的 程序集 dll库文件


            //回忆创建类库工程步骤
            //在解决方案中 右键 添加 新建项目 选择C#类库文件 输入类库名 选择类库路径 创建类库工程
            //类库工程里可以创建各种类 类库工程中的类是纯写逻辑用的 我们按照要求写入Player类和相关代码
            //右键类库工程 点击生成或者重新生成 右键类库工程 在文件资源管理器中打开文件夹 进入bin再进入Debug 就会看到类库工程的dll文件
            //这样就可以用反射的知识 调用类库工程中的逻辑内容

            //调用Assembly类的LoadFrom方法 加载一个指定程序集
            Assembly assembly = Assembly.LoadFrom(@"E:\GameDevelopment\GameDevelopmentLearning\C#\C#进阶\CSharp进阶教程\测试\bin\Debug\测试");
            //注意1:传入的路径是dll文件所在的路径 路径的最后要带上dll文件的文件名 也可以加上dll后缀
            //注意2:不能直接传入路径 因为\是转义字符 直接输入\不代表\
            //取消转义字符有两种方式:
            //1.使用\\ 意思是取消转义 这样一个\\就代表一个真正的\
            //2.在路径前加一个@

            //调用Assembly类的GetTypes方法 获得程序集中的所有类的Type
            //用一个Type类数组装载
            Type[] types = assembly.GetTypes();

            //遍历打印程序集中的所有类的Type
            for (int i = 0; i < types.Length; i++)
            {
                Console.WriteLine(types[i]);
                //MrTao.MyCustomAttribute
                //MrTao.Position
                //MrTao.Player
            }

            //通过反射 实例化一个程序集中的对象(player对象)

            //调用GetType()方法 通过传入类名 得到MrTao.Player类的Type
            //注意:类名必须包含命名空间 不然找不到
            Type playerType = assembly.GetType("MrTao.Player");

            //通过反射 实例化一个player对象
            //调用Activator类中的CreateInstance()方法 
            //Activator类中的CreateInstance()方法会返回一个object对象 CreateInstance有多个重载
            //第一个参数固定传 你想要构造的类的Type类 比如 实例化一个icon对象就传入iconType
            //后面的参数是变长参数 根据你想要构造的类 的 构造函数参数 传对应的参数 假如是无参构造函数就可以不传
            //Player类只有无参构造函数 所以就可以不传
            object playerObject = Activator.CreateInstance(playerType);
            //这样就能实例化player的对象 但是由于Player类不在我们的程序集中 所以没有as成Player 而是用object去装

            //打印playerObject
            Console.WriteLine(playerObject);//MrTao.Player
            #endregion
        }
    }
}

21.5 类库工程代码

using System;

namespace MrTao
{
    class MyCustomAttribute : Attribute
    {

    }

    // 位置
    struct Position
    {
        public int x;
        public int y;
        public Position(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }

    // 玩家
    class Player
    {
        [MyCustom()]
        public string name;

        public int hp;
        public int atk;
        public int def;
        public Position pos;
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏