重庆熊猫 Loading

.NET教程 - 反射 & 元数据(Reflection & Metadata)

更新记录
转载请注明出处:
2022年10月5日 发布。
2022年10月5日 从笔记迁移到博客。

反射(Reflection)介绍

说明

大多数情况下,我们都是运行程序集中的代码处理数据

但有时需要操作的是程序集中的代码本身(程序集和程序集中类型本身的信息)

有关程序及其类型的数据被称为元数据(Metadata),保存在程序集中

在程序运行过程中,可以查看自身或其他程序集的元数据

一个运行的程序查看本身的元数据或其他程序的元数据的行为叫做反射(reflection)

编译器生成的程序集中包含元数据(CIL类型定义及其描述),通过编程读取元数据

即通过编程来反过来读取程序自身这个数据(类型和元数据)

反射处理类所在的命名空间:

System.Reflection

反射定义在mscorlib.dll程序集中

常见用途:

​ 编译器读取程序集信息,并显示在IDE中,比如Visual Studio中的对象浏览器

​ 动态加载程序集

​ 插件式加载插件代码

Reflection 作用

Access the for types within an

获得程序集(assembly)的元数据(metadata)

包括:类型(type)、类型的成员(member)以及特性(attributes)

动态使用元数据(metadata)调用类型(type)

MemberInfo结构图

image

命名空间

System

System.Reflection

System.Reflection.Emit

相关类型

AssemblyName

使用该类可以发现大量隐藏在程序集中细节,如:版本信息、区域信息

MemberInfo

一个抽象的基类,为EventInfo、FieldInfo、MemberInfo和PropertyInfo类型定义了公共的行为

Module

一个抽象类,可以访问多文件程序集中给定的模块

ParameterInfo

一个类,保存给定参数的信息

PropertyInfo

一个抽象类,保存给定的属性信息

EventInfo

一个抽象类,用于保存给定的事件的信息

FieldInfo

一个抽象类,用于保存给定字段的信息

运行时类型判断

说明

在.NET中可以使用is、as、typeof来进行类型的判断

is运算符

通过is运算符,能够判断对象类型是否为特定类型,如果两种类型是相同类型

或者两者之间存在引用,装箱拆箱转换,则表明两种类型是兼容的

using System;

namespace ConsoleApp1
{
    public class A { }
    public class B : A { }
    public class Program
    {
        public static void Main(string[] args)
        {
            A a = new A();
            B b = new B();

            //这个打印,因为a 是 A 类型的对象
            if (a is A)
            {
                Console.WriteLine("a is an A");
            }

            //这个打印,因为b是B类型的对象,而B类型派生于A类型
            //由于b对象可以转换为A类型,因此b对象与A类型是兼容的,但是反过来就不成立,例如下面不打印
            if (b is A)
            {
                Console.WriteLine("b is an A because it is derived from");
            }

            //这个不打印
            //因为a不是B类型
            if (a is B)
            {
                
                Console.WriteLine("This won't display , because a not derived from B");
            }

            //这个打印
            //因为a默认继承自object类型
            if (a is object)
            {
                Console.WriteLine("a is an object");
            }

            Console.ReadKey();
        }
    }
}

as运算符

在运行期间执行类型转换,并且能够使得类型转换失败不抛异常,而返回一个null值

其实as也可以看作一个is运算符的简化备选方式

using System;

namespace ConsoleApp1
{
    public class A { }
    public class B : A { }
    public class Program
    {
        public static void Main(string[] args)
        {
            //创建类型的实例
            A a = new A();
            B b = new B();

            //先判断类型是否可以转换
            //然后进行强制转换
            if (a is B)
            {
                b = (B)a;
            }
            else
            {
                b = null;
            }

            //检测转换是否成功
            if (b == null)
            {
                Console.WriteLine("The cast in b=(B)a is not allowed");
            }

            //上面使用as运算符,能够把两部合二为一
            //as类型先检查强制类型转换的有效性,如果有效,则执行强类型转换过程
            b = a as B;
            if (b == null)
            {
                //这个打印
                Console.WriteLine("The cast in b=(B)a is not allowed");
            }
            Console.ReadKey();
        }
    }
}

typeof运算符

as、is 能够测试两种类型的兼容性

但大多数情况下,还需要获得某个类型的具体信息

这就用到了typeof,它可以返回与具体类型相关的System.Type对象

通过System.Type对象可以去顶此类型的特征

一旦获得给定类型的Type对象

就可以通过使用该对象定义的各种属性,字段,方法来获取类型的具体信息

Type类包含了很多成员,在接下来的反射中再详细讨论

using System;
using System.Text;

namespace ConsoleApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            //获得类型对应的Type类型
            Type t = typeof(StringBuilder);

            //获得类型的全名
            Console.WriteLine(t.FullName);
            
            //检测是否引用类型
            if (t.IsClass)
            {
                Console.WriteLine("is a class");
            }

            //检测是否密封类型
            if (t.IsSealed)
            {
                Console.WriteLine("is Sealed");
            }

            Console.ReadKey();
        }
    }
}

Type类型

说明

System.Type是一个抽象类,可以用来读取类型的元数据的信息

System.Reflection

注意:

​ 对于程序中的每个类型,CLR都会创建包含该类型信息的Type类型具体实例

​ 程序中每个类型都关联到独立的Type类型的实例

​ 不管创建的类型有多少个实例,只有一个Type对象会关联到所有实例

Type类是抽象类,不可以进行实例化,CLR在运行时创建其实例对象

当我们访问Type实例时,返回的是Type的引用,而不是其派生类

示意图:

image

type类型主要成员

image

属性:
IsAbstract
IsArray
IsClass
IsCOMObject
IsEnum
IsGenericTypeDefinition
IsGenericParameter
IsInterface
IsPrimitive
IsNestedPrivate
IsNestedPublic
IsSealed
IsValueType

方法:
GetConstructors()
GetEvents()
GetFields()
GetInterfaces()
GetMembers()
GetMethods()
GetNestedTypes()
GetProperties()
FindMembers()
GetType()
InvokeMember()

获取Type对象

获得对象对应的Type类型的方法

GetType()方法,GetType is evaluated at runtime

Typeof()运算符,typeof is evaluated statically at compile time

Object.GetType()

每个类型都继承自Object类型,Object类型定义了一个GetType方法,可以返回实例对象的Type对象引用

Type t = myInstance.GetType();

使用typeof()运算符

该运算符也是返回的是对应类型的Type对象的引用

注意:该运算符的参数是一个类型,不是一个实例

Type t = typeof(SomeClass);

System.Type.GetType()静态方法

System.Type.GetType(类型字符串) //注意参数是字符串,注意命名空间/区分大小写

Type t = Type.GetType("System.String");

Type类型实例-检测类型

检测是否抽象类型

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否抽象类型
Console.WriteLine(t.IsAbstract);

检测是否数组

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否数组
Console.WriteLine(t.IsArray);

检测是否类等引用类型

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否类
Console.WriteLine(t.IsClass);

检测是否枚举类型

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否枚举
Console.WriteLine(t.IsEnum);

检测是否接口类型

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否接口
Console.WriteLine(t.IsInterface);

检测是否指针类型

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否指针
Console.WriteLine(t.IsPointer);

检测是否密封

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否密封
Console.WriteLine(t.IsSealed);

检测是否值类型

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否值类型
Console.WriteLine(t.IsValueType);

检测类型是否公有

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否公有
Console.WriteLine(t.IsPublic);

检测类型是否非公有

string panda = "Panda666.com";
Type t = panda.GetType();
//检测是否非公有
Console.WriteLine(t.IsNotPublic);

Type类型实例-获得类型的成员

获得数据类型所在的命名空间

string panda = "Panda666.com";
Type t = panda.GetType();
//类型所在的命名空间
Console.WriteLine(t.Namespace);

获得数据类型的名称

string panda = "Panda666.com";
Type t = panda.GetType();
//类型的名称
Console.WriteLine(t.Name);

获得数据类型的名称(完全限定名,带命名空间)

string panda = "Panda666.com";
Type t = panda.GetType();
//类型的名称(带命名空间)
Console.WriteLine(t.FullName);

获得指定的字段信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得字段信息
FieldInfo fieldInfo = t.GetField("S");

获得所有字段信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得所有字段信息
FieldInfo[] fieldInfos = t.GetFields();

获得指定的方法信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得指定方法信息
MethodInfo methodInfo = t.GetMethod("Contains");
Console.WriteLine(methodInfo.Name);

反射中的GetMethods()方法不会包含扩展方法(extension methods)
可以去通过定义扩展方法的静态类去获取扩展方法(静态成员)
获得指定的方法信息(限制条件)

string panda = "Panda666.com";
Type t = panda.GetType();
//获得指定方法信息
MethodInfo methodInfo = t.GetMethod("Contains",BindingFlags.Public | BindingFlags.Instance);
Console.WriteLine(methodInfo.Name);

获得所有方法信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得所有方法信息
MethodInfo[] methodInfos = t.GetMethods();

获得属性信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得属性信息
PropertyInfo propertyInfo = t.GetProperty("A");

获得所有属性信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得所有属性信息
PropertyInfo[] propertyInfos = t.GetProperties();

获得事件信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得事件信息
EventInfo eventInfo = t.GetEvent("Click");

获得所有事件信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得所有事件信息
EventInfo[] eventInfos = t.GetEvents();

获得指定成员信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得指定成员信息
MemberInfo[] memberInfo = t.GetMember("sss");

获得所有成员信息

string panda = "Panda666.com";
Type t = panda.GetType();
//获得所有成员信息
MemberInfo[] memberInfos = t.GetMembers();

Type类型实例-调用类型的成员

调用类型的构造函数

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                if(type.Name == "PandaClass")
                {
                    //获得无参数的构造函数
                    ConstructorInfo? constructorInfo = type.GetConstructor(Type.EmptyTypes);
                    //检测是否存在该构造函数
                    if(constructorInfo != null)
                    {
                        //获得实例对象
                        object instance = constructorInfo.Invoke(Type.EmptyTypes);

                    }
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

读写类的属性

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                if(type.Name == "PandaClass")
                {
                    //获得无参数的构造函数
                    ConstructorInfo? constructorInfo = type.GetConstructor(Type.EmptyTypes);
                    //检测是否存在该构造函数
                    if(constructorInfo != null)
                    {
                        //获得实例对象
                        object instance = constructorInfo.Invoke(Type.EmptyTypes);

                        //获得该类型的属性
                        PropertyInfo propertyInfo = type.GetProperty("Code");

                        //设置该属性的值
                        propertyInfo.SetValue(instance, 888);

                        //读取该属性的值
                        var value = propertyInfo.GetValue(instance);

                        //输出属性值
                        Console.WriteLine((int)value);
                    }
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

调用实例方法(无返回值无参数)

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                if(type.Name == "PandaClass")
                {
                    //获得无参数的构造函数
                    ConstructorInfo? constructorInfo = type.GetConstructor(Type.EmptyTypes);
                    //检测是否存在该构造函数
                    if(constructorInfo != null)
                    {
                        //获得实例对象
                        object instance = constructorInfo.Invoke(Type.EmptyTypes);

                        //获得方法成员
                        MethodInfo methodInfo = type.GetMethod("PrintCode");

                        //调用方法(无参数无返回值)
                        methodInfo.Invoke(instance,Type.EmptyTypes);
                    }
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

调用实例方法(无返回有参数)

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                if(type.Name == "PandaClass")
                {
                    //获得无参数的构造函数
                    ConstructorInfo? constructorInfo = type.GetConstructor(Type.EmptyTypes);
                    //检测是否存在该构造函数
                    if(constructorInfo != null)
                    {
                        //获得实例对象
                        object instance = constructorInfo.Invoke(Type.EmptyTypes);

                        //获得方法成员
                        MethodInfo methodInfo = type.GetMethod("PrintName");

                        //调用方法(有参数无返回值)
                        methodInfo.Invoke(instance, new object[] { "Panda666" });
                    }
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

调用实例方法(有返回值有参数)

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                if(type.Name == "PandaClass")
                {
                    //获得无参数的构造函数
                    ConstructorInfo? constructorInfo = type.GetConstructor(Type.EmptyTypes);
                    //检测是否存在该构造函数
                    if(constructorInfo != null)
                    {
                        //获得实例对象
                        object instance = constructorInfo.Invoke(Type.EmptyTypes);

                        //获得方法成员
                        MethodInfo methodInfo = type.GetMethod("MethodName");

                        //调用方法(有参数有返回值)
                        object? returnValue = methodInfo.Invoke(instance, new object[] { "Panda666" });
                        
                        //读取方法的返回值
                        if(returnValue is bool)
                        {
                            Console.WriteLine((bool)returnValue);
                        }
                    }
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

调用静态方法

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                if(type.Name == "PandaClass")
                {
                    //注意:调用静态方法无需创建类型的实例

                    //获得静态方法
                    MethodInfo methodInfo = type.GetMethod("PrintHello",BindingFlags.Static | BindingFlags.Public);

                    //调用静态方法
						//本质和调用实例方法一样,第一个参数设置为null即可
                    methodInfo.Invoke(null, new object[] { "Panda" });
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

Activator Class

说明

可以动态实例化一个类型

实例:动态实例化类型

using System;
using System.Reflection;

namespace PandaTestClass
{
    /// <summary>
    /// 用户测试的类型
    /// </summary>
    public class PandaClass
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }

        public PandaClass(int id,string name,int age)
        {
            this.Id = id;
            this.Name = name;
            this.Age = age;
        }

        public string DoSomething(string arg)
        {
            Console.WriteLine(arg);
            return arg;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //获得PandaClass对应的Type类型
            Type type = typeof(PandaClass);
            //创建PandaClass的实例
            object o = Activator.CreateInstance(type, new object[] { 666, "Panda", 18 });
            //获得PandaClass实例的方法
            MethodInfo method = type.GetMethod("DoSomething");
            //调用PandaClass的方法
            method.Invoke(o, new object[] { "PandaArgs" });
            //获得PandaClass类的属性
            PropertyInfo p = type.GetRuntimeProperty("Name");
            Console.WriteLine((string)p.GetValue(o));

            //wait
            Console.ReadKey();
        }
    }
}

Assembly Class

说明

一个抽象类
处理和操作程序集
包含许多静态方法,通过该类可以加载、了解和操作一个程序集
主要用于操作、动态加载程序集

所在命名空间

System.Reflection;

实例

动态加载程序集

//加载指定程序集(使用完整的程序集名称)
Assembly.Load("AssmeblyName");

注意:运行库会自动查找本地目录和全局缓存

//加载指定程序集(指定程序集文件路径)
Assembly.LoadFrom("FilePath.dll");

获得程序集名称

Assembly assembly = Assembly.LoadFrom("filePath");
//获得程序集名称
string fullName = assembly.FullName;

获得程序集中所有类型信息

Assembly assembly = Assembly.LoadFrom("filePath");
//获得程序集中所有类型信息
Type[] types = assembly.GetTypes();

加载程序集dll文件并读取类型

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                //检测类型是否为Class,delegate类型,非值类型
                if (type.IsClass)
                {
                    Console.WriteLine(type.FullName);
                }
                //检测类型是否为值类型
                else if (type.IsValueType)
                {
                    Console.WriteLine(type.FullName);
                }
                //检测是否为Enum类型
                else if(type.IsEnum)
                {
                    Console.WriteLine(type.FullName);
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

读取类型的成员

using System;
using System.Reflection;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //加载程序集dll文件
            Assembly assembly = Assembly.LoadFrom("PandaTestLibrary.dll");
            //获得程序集中的类型
            foreach (Type type in assembly.GetTypes())
            {
                if(type.Name == "PandaClass")
                {
                    //获得字段成员
                    FieldInfo fieldInfo = type.GetField("Field");
                    //获得属性成员
                    PropertyInfo propertyInfo = type.GetProperty("Code");
                    //获得方法成员
                    MethodInfo methodInfo = type.GetMethod("Do");
                    //获得方法的参数
                    ParameterInfo[] parameterInfos = methodInfo.GetParameters();
                    //输出方法参数
                    Console.WriteLine(parameterInfos[0].Name); //方法参数名称
                    Console.WriteLine(parameterInfos[0].ParameterType); //方法参数类型
                    //获得事件成员
                    EventInfo eventInfo = type.GetEvent("Event");
                    //获得构造函数成员
                    ConstructorInfo[] constructorInfos = type.GetConstructors();
                }
            }

            //wait
            Console.WriteLine("Success");
            Console.ReadKey();
        }
    }
}

创建程序集中指定类型的实例

Assembly assembly = Assembly.LoadFrom("filePath");
//创建指定类型实例
object s = assembly.CreateInstance("abc");
创建当前程序集中类型的实例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace ConsoleApp1
{

    class Program
    {
        static void Main(string[] args)
        {

            //获得当前正在执行的程序集
            Assembly executingassembly = Assembly.GetExecutingAssembly();
            //创建指定类型的实例
            var Panda = executingassembly.CreateInstance("ConsoleApp1.PandaTestClass");
            //转换为实际类型进行使用
            if(Panda is PandaTestClass)
            {
                PandaTestClass trueInstance = Panda as PandaTestClass;
                trueInstance.DoSomething();
            }

            Console.ReadKey();
        } 
    }

    //测试使用的类型
    class PandaTestClass
    {
        public void DoSomething()
        {
            Console.WriteLine("Do Something!");
        }
    }
}

反射与泛型(Reflection with Generics)

反射与泛型

实例:检测是否泛型类型

using System;
public class Program
{
  static void Main()
  {
      Type type = typeof(System.Nullable<>);
      Console.WriteLine(type.ContainsGenericParameters);
      Console.WriteLine(type.IsGenericType);

      type = typeof(System.Nullable<DateTime>);
      Console.WriteLine(type.ContainsGenericParameters);
      Console.WriteLine(type.IsGenericType);
  }
}
/*
OUTPUT
True
True
False
True */

使用反射获得泛型参数的具体类型(Using Reflection with Generic Types)

实例:

using System;
using System.Collections.Generic;

public partial class Program
{
  public static void Main()
  {

      Stack<int> s = new Stack<int>();

      Type t = s.GetType();

      foreach(Type type in t.GetGenericArguments())
      {
          System.Console.WriteLine(
              "Type parameter: " + type.FullName);
      }
      // ...
  }
}

/*
OUTPUT
Type parameter: System.Int32
*/
posted @ 2022-10-05 09:48  重庆熊猫  阅读(193)  评论(0编辑  收藏  举报