C#反射相关基础

#本文概括

类型

执行目标

目标举例

描述

使用

Type

public class A

包含类型的特性

Assembly

程序集

ClassLibrary.dll

表示一个程序集

FieldInfo

类中字段

public string name;

发现字段的属性并提供对字段元数据的访问权限

MemberInfo

成员

所有成员

获取有关成员属性的信息并提供对成员元数据的访问权限

MethodInfo

方法

public string AddValue(string prefix)//自定义函数

{ return value + "+" + prefix; }

发现方法的属性并提供对方法元数据的访问

Activator

Type

Type t = Type.GetType("Test.TestC");

实例化类型Type

一、命名空间:System.Reflection

#概述

在C#中,我们要使用反射,首先要搞清楚以下命名空间中几个类的关系:

System.Reflection命名空间
(1) AppDomain:应用程序域,可以将其理解为一组程序集的逻辑容器
(2) Assembly:程序集类
(3) Module:模块类
(4) Type:使用反射得到类型信息的最核心的类

他们之间是一种从属关系,也就是说,一个AppDomain可以包含n个Assembly,一个Assembly可以包含n个Module,而一个Module可以包含n个Type。

*Type类型

1)概述

命名空间:System (Type类在更多地方使用,而不仅仅是System.Reflection)

程序集:netstandard.dll

官方文档:链接地址

Type类,用来包含类型的特性。对于程序中的每一个类型,都会有一个包含这个类型的信息的Type类的对象,类型信息包含数据,属性和方法等信息。

Type是很多反射功能的入口,它实现很多方法和属性,可用的属性都是只读的:可以使用Type确定数据的类型,但不能使用它修改该类型。

2)通过反射获取类型(生成Type类的对象)

通过反射获取类型

【通过类名获取类别】

System.Type t = typeof(PersonNameSpace.Person)

//举例:Type t2 = typeof(double);

【通过对象,获取类别】

PersonNameSpace.Person p = new PersonNameSpace.Person();

System.Type t = p.GetType();

//举例:int i = 41;

Type t1 = i.GetType();

注意:该情况下,返回的Type对象只与该数据类型相关,不包含该类型实例的任何信息

【通过类名的字符串,获取类别】

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

//举例:Type t3 = Type.GetType("System.Double");

参数

情况一:是正在执行的程序集mscorlib.dll/System.Private.CoreLib.dl装配件中声明的类型皆可以省略装配件名称(.Net装配件编译的时候,默认都引用了mscorlib.dll,除非在编译的时候明确指定不引用它)

->命名空间限定的类型名称,即”命名空间.类名“

情况二:其他

->要获取的类型的程序集限定名称

System.Double是在mscorlib.dll中声明的,故Type t3 = Type.GetType(“System.Double”)是正确的
System.Data.DataTable是在System.Data.dll中声明的,那么:Type.GetType(“System.Data.DataTable”)就只能得到空引用,
必须:Type t = Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");这样才可以

若这里仍不清楚,可跳至最后了解代码实际操作进行理解。

3)属性

返回类型

方法名称

描述

String

Name

数据类型名

String?

FullName

数据类型的完全名(包含名称空间)【全名

String?

Namespace

定义数据类型的名称空间名

Type?

BaseType

父类类型

Type

UnderlyingSystemType

该Type在.NET运行库中映射到的类型

bool

IsAbstract,IsArray,IsClass,IsEnum等

判断Type是什么类型的属性

类型后加"?",表示可为空的类型。

4)方法

操作目标

返回值

方法名称

描述

成员

MemberInfo?

GetMember(string name)

搜索具有指定名称的公共成员

MemberInfo[]

GetMembers()

返回为当前 Type所有公共成员(不包含构造函数)

方法

MethodrInfo

GetMethod(string name)

搜索具有指定名称的公共方法

MethodrInfo[]

GetMethods()

返回为当前 Type所有公共方法

字段

FieldInfo?

GetField(string name)

用于取得该类的字段(成员变量)的信息

FieldInfo[]

GetFields()

访问所有字段包括静态和非静态

5)实验

  • 代码 1

  • 展示Type部分属性

namespace ConsoleApp1
{
    public class Program
    {
        static void Main(String[] args)
        {
            Console.WriteLine("\t\t\t数据类型名\t数据类型的完全限定名(包含名称空间)\t"
            +"在其中定义数据类型的名称空间名\t该对象的直接基本类型\t该Type在.NET运行库中映射到的类型");
            //对象调用GetType()函数:
            int i = 41;
            Type t1 = i.GetType();
            Console.WriteLine(t1+"\t\t" + 
                              t1.Name+ "\t\t\t" + 
                              t1.FullName + "\t\t\t\t" + 
                              t1.Namespace+ "\t\t\t" + 
                              t1.BaseType + "\t" + 
                              t1.UnderlyingSystemType);
            //使用typeof运算符,这个运算符的参数是类型的名称,但不放在引号中:
            Type t2 = typeof(A);
            Console.WriteLine(t2+"\t" + 
                              t2.Name + "\t\t\t" + 
                              t2.FullName + "\t\t\t" + 
                              t2.Namespace + "\t\t" + 
                              t2.BaseType + "\t\t" + 
                              t2.UnderlyingSystemType);
            /* Console.WriteLine(t.id); */ //报错,不可使用A类型实例的任何信息
            //调用Type类的静态方法GetType():
            Type t3 = Type.GetType("System.Double");
            Console.WriteLine(t3+"\t\t" + 
                              t3.Name + "\t\t\t" + 
                              t3.FullName + "\t\t\t\t" + 
                              t3.Namespace + "\t\t\t" + 
                              t3.BaseType + "\t" + 
                              t3.UnderlyingSystemType);
        }
        //自定义类
        public class A
        {
            public int id { get; set; }
        }
    }
}
  • 结果

  • 代码2

  • 实验方法

using System.Reflection;
namespace ConsoleApp1
{
    public class Program
    {
        static void Main(String[] args)
        {
            //1.创建Type对象,类为同程序集下Test命名空间里的TestC
            Type t = Type.GetType("Test.TestC");
            //2.获取Type对象的所有公共成员
            MemberInfo[] ms = t.GetMembers();
            //循环打印所有公共成员
            foreach (var m in ms)
            {
                Console.WriteLine(m);
            }
        }
    }
}
//另一个命名空间
namespace Test
{
    //一个简单的类,包含一个无参的构造器,一个有参数的构造器,一个GetValue的方法,一个value属性
    public class TestC
    {
        //自定义字段
        public string value { get; set; }
        //构造函数
        public TestC()
        { Console.WriteLine("无参的构造函数"); }
        public TestC(string v)
        {
            value = v;
        }
        //自定义函数
        public string GetValue(string prefix)
        {
            return "NULL";
        }
        private string GetVs()
        {
            return "Vs";
        }
    }
}
  • 结果

1.Assembly 程序集

1)概述

官方文档:链接地址

Assembly =装配件 = 程序集。表示一个程序集,它是一个可重用、无版本冲突并且可自我描述的公共语言运行时应用程序构建基块。(编译出来的.dll、.exe

  命名空间:System.Reflection

  程序集:System.Runtime.dll

使用 Assembly 类加载程序集、浏览程序集的元数据和构成部分、发现程序集中包含的类型以及创建这些类型的实例。

程序集主要分为两种类型:强命名程序集、弱命名程序集。这两种程序集的结构上完全一致的。两者真正的区别在于强命名程序集用发布者的公钥/私钥允许对程序集的惟一标识。
1、强命名程序集
一个强命名程序集包括4个重要属性:一个文件名(无扩展名)、一个版本号、一个语言文化标识以有一个公钥。這些属性惟一地标识了程序集。
2、弱命名程序集
简单地说,没有以上强命名程序集所有的属性就是弱命名程序集.

2)动态加载程序集

Load、LoadFrom和LoadWithPartialName

动态加载程序集

(using System.Reflection;)

【利用Load方法】

Assembly a = Assembly.Load("Y_TestProject");

//错误举例“Assembly.Load("Y_TestProject.dll");"报错

注意:

  1. 参数不加扩展名

  1. 只能读本项目内的程序集,不能读复制过来的

参数:

强程序集:提供程序集的详细信息,例“MyAssembly,Version=1.0.0.0,culture=zhCN,PublicKeyToken=47887f89771bc57f”

弱程序集:程序集的名称【不包含扩展名

【利用LoadForm方法】

Assembly a = Assembly.LoadFrom("Y_TestProject.dll");

//也可以Assembly a = Assembly.LoadFrom("Y_TestProject");

注意:

  1. 参数加/不加扩展名

  1. 本项目内的、复制过来的程序集都可以读取

参数:指定一个程序集文件的路径名【扩展名or不包含

......

...

若这里仍不清楚,可跳至最后了解代码实际操作进行理解。

3)方法

返回值

方法名称

描述

Type

GetType(String)

注意:详情请看上面"通过反射获取类型(生成Type类的对象)"

4)实验

  • 代码

  • 框架

  • 主项目——控制台

using System.Reflection;

namespace Y_TestProject
{
    public class Program
    {
        static void Main(string[] args)
        {
            /*--获取本项目的程序集,获取其下指定类,调用指定方法--*/
            Assembly a = Assembly.LoadFrom("Y_TestProject.dll");
            //“Assembly.Load("Y_TestProject.dll");"报错,系统找不到指定的文件
            //Type t0 =  a.GetType("AC");   //t为null
            Type t1 = a.GetType("Y_TestProject.AC");    //正确
            MethodInfo mi1 = t1.GetMethod("ACP");
            mi1.Invoke(Activator.CreateInstance(t1), null);

            /*--获取导入的dll文件,获取其下指定类,调用指定方法--*/
            //Assembly aClassLibrary0 = Assembly.Load("ClassLibrary.dll");//报错,系统找不到指定的文件
            Assembly aClassLibrary1 = Assembly.LoadFrom("ClassLibrary.dll"); //正确
            Type t3 = aClassLibrary1.GetType("LeiKu.Pub");  //获取类型
            MethodInfo mi2= t3.GetMethod("Printf");  //获取方法
            mi2.Invoke(Activator.CreateInstance(t3),new object[] { "调用成功" });    //方法调用

            Console.ReadLine();
        }
    }
    public class AC
    {
        public string A;

        public void ACP()
        {
            Console.WriteLine("ACP");
        }
    }
}
  • 引入的程序集代码

namespace LeiKu
{
    internal class Pub
    {
        public void Printf(string msg)
        {
            Console.BackgroundColor = ConsoleColor.Green;   //改控制台背景元素
            Console.WriteLine(msg); //打印信息
        }
    }
}
  • 结果

2.FieldInfo 字段

命名空间:System.Reflection

官网文档:地址链接

1)概述

发现字段的属性并提供对字段元数据的访问权限。

2)基本使用

获取字段

t.GetField(字段名,访问权限BindingFlags | 静态非静态BindingFlags)

读写字段

fi.SetValue(对象) //给对象字段

object val = fi.GetValue(对象object) //获取对象字段

//静态字段不需要对象,设置为null

获取字段特性

Attributes获取与此字段关联的特性

CustomAttributes获取包含此成员自定义属性的集合

3)实验

  • 代码

using System.Reflection;
using Test;
namespace ConsoleApp1
{
    public class Program
    {
        static void Main(String[] args)
        {
            // 获取类型
            Type t = typeof(Test.TestC);   
            // 获取类型t中的name字段
            FieldInfo fi = t.GetField("name");  //t.GetField("value")为null
            // 实例化单例对象
            object j = Activator.CreateInstance(t);    //实例化
            // 设置单例对象的name字段的值
            fi.SetValue(j,"123");
            // 打印
            Console.WriteLine(fi.GetValue(j));  //打印name字段的值
            Console.WriteLine(fi.Name);         //打印fi对应的字段名称
        }
    }
}
//另一个命名空间
namespace Test
{
    public class TestC
    {
        //字段 
        public string name = "cml";
        public string value { get; set; }
    }
}
  • 结果

3.MemberInfo 成员

命名空间:System.Reflection

官网文档:地址链接

1)概述

获取有关成员属性的信息并提供对成员元数据的访问权限。

4.MethodInfo 方法

命名空间:System.Reflection

官网文档:地址链接

1)概述

发现方法的属性并提供对方法元数据的访问。

2)方法

返回值

方法名称

描述

object?

Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)

当在派生类中重写时,调用具有给定参数的反射的方法或构造函数。

(继承自 MethodBase)

【调用方法/构造函数,该方法/构造函数具有参数,以object类型返回调用完后方法/构造函数返回的结果】

3)基本使用

获取方法

t.GetMethod(String,BindingFlags,Binder,Type[],ParameterModifier[])

调用方法

info.Invoke(Object,BindingFlags,Binder,Object[],CultureInfo)

获取方法特性

Attributes获取与此字段关联的特性

CustomAttributes获取包含此成员自定义属性的集合

4)实验

  • 代码

using System.Reflection;
using Test;
namespace ConsoleApp1
{
    public class Program
    {
        static void Main(String[] args)
        {
            //1.创建对象
            Type t = typeof(Test.TestC);    //获取类型
            object j = Activator.CreateInstance(t,new object[] {"cml"});    //实例化
            //2.获取目标类型的公共方法,方法名为AddValue
            MethodInfo method = t.GetMethod("AddValue");
            //3.目标对象调用method指定的方法
            object msg =  method.Invoke(j, new object[] { "Hello world !" });   //参数:实例化对象,目标方法参数
            Console.WriteLine("msg:\t\t" + msg.ToString());
            //4.打印对象的字段
            TestC c = (TestC)j;
            Console.WriteLine("c.value:\t" + c.value);
        }
    }
}
//另一个命名空间
namespace Test
{
    public class TestC
    {
        //字段 
        public string value { get; set; }
        //构造函数
        public TestC() 
        { }
        public TestC(string v)  
        {
            value = v;
        }
        //自定义函数
        public string AddValue(string prefix)
        {
            return value + "+" + prefix;   //将value与该方法传入参数拼接
        }

    }
}
  • 结果

5.BindingFlags

1)概述

命名空间:System.Reflection

官网文档:地址链接

搜索使用的枚举值

BindingFlags

(表示搜索的按位组合)

Public(公共)16

NonPublic(私有)32

Static(静态)8

Instance(非静态)4

组合:eg私有的且非静态的NonPublic Instance

NonPublic | Instance

0010 0000

| 0000 0100

——————————

0010 0000

BindingFlags详情相关可查看大佬文章:地址链接

二、其他相关

1.Activator

命名空间:System

官网文档:地址链接

1)概述

包含特定的方法,用以在本地或从远程 创建对象类型,或 获取对现有远程对象的引用。 此类不能被继承。

2)方法

返回值

方法名称

描述

object?

CreateInstance(Type t)

使用目标类为Public且无参的构造函数实例化对象

object?

CreateInstance(Type t,params object[] args)

使用目标类为Public且有参的构造函数实例化对象

object?

CreateInstance(Type t,bool nonPublic)

公共或非公共默认构造函数都可以匹配,则为 true;

只有公共默认构造函数可以匹配,则为 false

object?

CreateInstance(Type t, BindingFlags bindingAttr, Binder? binder,object[] args,CultureInfo)

使用目标类为bindingAttr指定且有参构造函数实例化对象

【注意:目标类中不同的公共/非公共和有参/无参的构造函数应选择对应正确的方法实例化对象,否则"System.MissingMethodException:“Constructor on type 'Test.TestC' not found.”报错"。】

3)实验

  • 代码 在运行时动态构造对象

using System.Reflection;
using Test;
namespace ConsoleApp1
{
    public class Program
    {
        static void Main(String[] args)
        {
            //相当于Person p1=new Person ();
            //通过反射,构造实例,返回的是一个object对象,接收存储
            object p;
            //1.通过反射,使用 公有、无参 构造函数实例化对象
            p = Activator.CreateInstance(typeof(TestC));

            TestC c1 = p as TestC;        //转化
            Console.WriteLine(c1.Value);    //NULL证明调用了默认构造函数

            //2.通过反射,使用 公有、有参 构造函数实例化对象
            p = Activator.CreateInstance(typeof(TestC), new object[] {"123"});

            TestC c2 = p as TestC;
            Console.WriteLine(c2.Value);    //123 证明调用了public、有参构造函数

            //3.通过反射,使用 公有/非公有、无参 构造函数实例化对象
            p = Activator.CreateInstance(typeof(TestC), false);

            TestC c3 = p as TestC;
            Console.WriteLine(c3.Value);    //NULL证明调用了默认构造函数

            //4.通过反射,调用 非共有、实例、有参 构造函数实例化对象----eg:私有的有参构造调用
            p = Activator.CreateInstance(typeof(TestC), BindingFlags.NonPublic|BindingFlags.Instance, null, new object[] { "444","555" }, null);

            TestC c4 = p as TestC;
            Console.WriteLine(c4.Value);    //444+555 证明调用了private、有两参构造函数

            Console.Read();
        }
    }
}
//另一个命名空间
namespace Test
{
    //一个简单的类,包含一个无参的构造器,一个有参数的构造器,一个GetValue的方法,一个Value属性
    public class TestC
    {
        public string _value { get; set; }
        public string Value
        {
            set
            {
                _value = value;
            }
            get
            {
                if (_value == null)
                    return "NULL";
                else
                    return _value;
            }
        }
        public TestC()  //无参的构造函数
        { }
        public TestC(string v)  //带参的构造函数
        {
            _value = v;
        }
        private TestC(string v1, string v2)
        {
            _value = v1+"+"+v2;
        }
    }
}
  • 结果

学习大佬文章 https://www.cnblogs.com/afei-24/p/6867986.html

https://blog.csdn.net/qq_45505703/article/details/102668091

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值