C# 反射(Reflection)详解-Assembly

目录

介绍

反射中的运行时类型

Assembly案例

1.GetFiles

2.GetManifestResourceNames

3.GetReferencedAssemblies

4.GetTypes

5.Load

6.LoadFile

7.CreateInstance

8.LoadFrom

结束


介绍

微软关于反射文档:点击跳转

下面是微软官方的解释

System.Reflection 命名空间中的类与 System.Type 使你能够获取有关加载的程序集和其中定义的类型的信息,如类、接口和值类型(即结构和枚举)。 可以使用反射在运行时创建、调用和访问类型实例。 有关反射的特定方面的主题,请参见本概述末的相关主题。

公共语言运行时加载程序管理应用程序域,应用程序域构成具有相同应用程序范围的对象周围定义的边界。 此管理包括将每个程序集加载到相应的应用程序域中和控制每个程序集内的类型层次结构的内存布局。

程序集包含模块、模块包含类型,而类型包含成员。 反射提供封装程序集、模块和类型的对象。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。 然后,可以调用类型的方法或访问其字段和属性。 反射的典型用法如下所示:

  • 使用 Assembly 来定义和加载程序集,加载程序集清单中列出的模块,以及在此程序集中定位一个类型并创建一个它的实例。

  • 使用 Module 发现信息,如包含模块的程序集和模块中的类。 还可以获取所有全局方法或模块上定义的其它特定的非全局方法。

  • 使用 ConstructorInfo 发现信息,如名称、参数、访问修饰符(如 public 或 private)和构造函数的实现详细信息(如 abstract 或 virtual)。 使用 Type 的 GetConstructors 或 GetConstructor 方法来调用特定构造函数。

  • 使用 MethodInfo 发现信息,如名称、返回类型、参数、访问修饰符(如 public 或 private)和方法的实现详细信息(如 abstract 或 virtual)。 使用 Type 的 GetMethods 或 GetMethod 方法来调用特定方法。

  • 使用 FieldInfo 发现信息,如名称、访问修饰符(如 public 或 private)和一个字段的实现详细信息 (如 static);并获取或设置字段值。

  • 使用 EventInfo 发现信息(如名称、事件处理程序的数据类型、自定义特性、声明类型以及事件的反射的类型),并添加或删除事件处理程序。

  • 使用 PropertyInfo 发现信息(如名称、数据类型、声明类型,反射的类型和属性的只读或可写状态),并获取或设置属性值。

  • 使用 ParameterInfo 发现信息,如参数的名称、数据类型、参数是输入参数还是输出参数以及参数在方法签名中的位置。

  • 使用 CustomAttributeData 在于应用程序域的仅反射上下文中工作时发现有关自定义特性的信息。 CustomAttributeData 使你能够检查特性,而无需创建它们的实例。 System.Reflection.Emit 命名空间的类提供一种专用形式的反射,使你能够在运行时生成类型。

还可以使用反射来创建称为类型浏览器的应用程序,它使用户能够选择类型,然后查看有关这些类型的信息。

反射还有其它用途。 JScript 等语言的编译器使用反射来构造符号表。 System.Runtime.Serialization 命名空间中的类使用反射来访问数据并确定要保存哪些字段。 System.Runtime.Remoting 命名空间中的类通过序列化间接使用反射。

反射中的运行时类型

反射提供类(如 Type 和 MethodInfo),用于表示类型、成员、参数和其它代码实体。 但使用反射时,你并不直接使用这些类,其中大部分类均是抽象的(Visual Basic 中为 MustInherit)。 相反,你使用由公共语言运行时 (CLR) 提供的类型。

例如,使用 C# typeof 运算符(Visual Basic 中为 GetType)获取 Type 对象时,该对象实际上是 RuntimeType。 RuntimeType 派生自 Type,并提供所有抽象方法的实现。

这些运行时类是 internal(Visual Basic 中为 Friend)。 它们没有与其基类分开记录,因为它们的行为由基类文档来描述。

Title 说明
查看类型信息 介绍 Type 类,并提供演示如何使用具有几个反射类的 Type 来获取有关构造函数、方法、字段、属性和事件的信息的代码示例。
反射类型和泛型类型 说明反射如何处理泛型类型和泛型方法的类型参数和类型自变量。
反射的安全注意事项 描述确定可以在何种程度上使用反射来发现类型信息和访问类型的规则。
动态加载和使用类型 描述支持后期绑定的反射自定义绑定接口。
如何:将程序集加载到仅反射上下文中 描述仅反射的加载上下文。 显示如何加载程序集、如何测试上下文以及如何检查应用到仅反射上下文中的程序集。
如何:使用 MetadataLoadContext 检查程序集内容 使用 MetadataLoadContext 加载和检查程序集。
访问自定义特性 演示如何使用反射来查询特性的存在和值。
指定完全限定的类型名称 描述 Backus-Naur 形式 (BNF) 的完全限定类型名称的格式,以及指定特殊字符、程序集名称、指针、引用和数组所需的语法。
如何:使用反射挂接委托 说明如何创建方法的委托并将委托挂钩到事件。 说明如何使用 DynamicMethod 在运行时创建事件处理方法。
发出动态方法和程序集 说明如何生成动态程序集和动态方法。

在此,我将 Assembly,Module,ConstructorInfo,MethodInfo,FieldInfo,EventInfo,PropertyInfo,ParameterInfo,CustomAttributeData 这几个模块逐个写成帖子,每个帖子单独介绍其中的一个模块,其实每个模块的 API 接口非常多,我这里就不能一一介绍了,由于我们开发中反射用的并不多,我也不能真正理解每个接口的精髓之处,不足之处,还望各位大佬多多指点。

Assembly案例

先看一些简单的使用

namespace 计算3
{
    public class Program
    {
        static void Main(string[] args)
        {
            Assembly assem = Assembly.GetExecutingAssembly();
            Console.WriteLine("程序集的名称:" + assem.FullName);
            Console.WriteLine("版本:" + assem.GetName().Version);
            Console.WriteLine("程序集的位置:" + assem.CodeBase);
            Console.WriteLine("程序的完整路径:" + assem.Location);
            Console.WriteLine("此程序集的入口点:" + assem.EntryPoint);
            Type[] types = assem.GetTypes();
            foreach (var item in types)
            {
                Console.WriteLine("类:" + item.Name);
            }

            Console.ReadKey();
        }
    }
}

输出

C# 反射(Reflection)详解-Assembly_第1张图片

1.GetFiles

获取此程序集清单的文件表中指定文件的 System.IO.FileStream。

官方文档:点击跳转

namespace 计算1
{
    class Program
    {
        static void Main(string[] args)
        {
            //获取默认应用程序域中的进程可执行文件
            Assembly assembly = Assembly.GetEntryAssembly();
            //获取程序集清单文件表中的文件,可以指定是否包括资源模块
            FileStream[] fileStreams = assembly.GetFiles();
            foreach (FileStream fileStream in fileStreams)
            {
                Console.WriteLine(fileStream.Name);
            }

            Console.ReadKey();
        }
    }
}

输出

C# 反射(Reflection)详解-Assembly_第2张图片

2.GetManifestResourceNames

返回此程序集中的所有资源的名称。

官方文档:点击跳转

这个接口在 Winform 中比较合适,因为 Winform 中自带 Resources 文件

C# 反射(Reflection)详解-Assembly_第3张图片

将程序的输出类型设置为控制台输出

namespace 计算2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //获取默认应用程序域中的进程可执行文件
            Assembly assembly = Assembly.GetEntryAssembly();
            //此程序集中的所有资源的名称
            string[] names = assembly.GetManifestResourceNames();
            foreach (var name in names)
            {
                Console.WriteLine("资源:" + name);
            }
        }
    }
}

输出

C# 反射(Reflection)详解-Assembly_第4张图片

3.GetReferencedAssemblies

 获取此程序集引用的所有程序集的 System.Reflection.AssemblyName 对象

官方文档:点击跳转

namespace 计算1
{
    class Program
    {
        static void Main(string[] args)
        {
            //获取默认应用程序域中的进程可执行文件
            Assembly assembly = Assembly.GetEntryAssembly();
            //此程序集引用的所有程序集
            AssemblyName[] assemblyNames = assembly.GetReferencedAssemblies();
            foreach (AssemblyName assemblyName in assemblyNames)
            {
                Console.WriteLine(assemblyName.FullName);//程序集全名
            }

            Console.ReadKey();
        }
    }
}

输出

4.GetTypes

获取此程序集中定义的类型。

官方文档:点击跳转

先看看当前程序集有那些类

C# 反射(Reflection)详解-Assembly_第5张图片

namespace 计算1
{
    class Program
    {
        static void Main(string[] args)
        {
            //获取默认应用程序域中的进程可执行文件
            Assembly assembly = Assembly.GetEntryAssembly();
            //此程序集中定义的类型
            Type[] types = assembly.GetTypes();
            foreach (Type type in types)
            {
                Console.WriteLine(type.FullName);
            }

            Console.ReadKey();
        }
    }
}

 输出

C# 反射(Reflection)详解-Assembly_第6张图片

5.Load

加载程序集

官方文档:点击跳转

namespace 计算1
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.Load("计算1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
            Console.WriteLine(assembly.FullName);

            Console.ReadKey();
        }
    }
}

输出

6.LoadFile

加载指定路径上的程序集文件的内容。

官方文档:点击跳转

namespace 计算1
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.LoadFile(Environment.CurrentDirectory + "\\ClassLibraryTest.dll");
            Console.WriteLine(assembly.FullName);

            Console.ReadKey();
        }
    }
}

输出:

7.CreateInstance

使用区分大小写的搜索,从此程序集中查找指定的类型,然后使用系统激活器创建它的实例。

官方文档:点击跳转

项目的结构,其中的 TList ,可以看我另一个帖子:点击跳转

C# 反射(Reflection)详解-Assembly_第7张图片

namespace 计算1
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = typeof(TList).Assembly;
            TList list = (TList)assembly.CreateInstance(typeof(TList).FullName);
            list.Add(344);
            Console.WriteLine(list.Count);

            Console.ReadKey();
        }
    }
}

输出

C# 反射(Reflection)详解-Assembly_第8张图片

8.LoadFrom

已知程序集的文件名或路径,加载程序集。

官方文档:点击跳转

namespace 计算1
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly assembly = Assembly.LoadFrom(Environment.CurrentDirectory + "\\ClassLibraryTest.dll");
            Console.WriteLine(assembly.FullName);

            Console.ReadKey();
        }
    }
}

输出

这里可以看出,LoadFrom 和 LoadFile 效果差不多,那么区别在哪里呢

1、Assembly.LoadFile只载入相应的dll文件,比如Assembly.LoadFile("abc.dll"),则载入abc.dll,假如abc.dll中引用了def.dll的话,def.dll并不会被载入。
Assembly.LoadFrom则不一样,它会载入dll文件及其引用的其他dll,比如上面的例子,def.dll也会被载入。

2、用Assembly.LoadFrom载入一个Assembly时,会先检查前面是否已经载入过相同名字的Assembly,比如abc.dll有两个版本(版本1在目录1下,版本2放在目录2下),程序一开始时载入了版本1,当使用Assembly.LoadFrom("2\\abc.dll")载入版本2时,不能载入,而是返回版本1。
Assembly.LoadFile的话则不会做这样的检查,比如上面的例子换成Assembly.LoadFile的话,则能正确载入版本2。
LoadFile:加载指定路径上的程序集文件的内容。LoadFrom: 根据程序集的文件名加载程序集文件的内容。

区别:

LoadFile 方法用来来加载和检查具有相同标识但位于不同路径中的程序集.但不会加载程序的依赖项。
LoadFrom 不能用于加载标识相同但路径不同的程序集。

结束

如果这个帖子对你有用,欢迎 关注 + 点赞 + 留言,谢谢

end

你可能感兴趣的:(C#,c#)