在上一章中,我们探究了 C# 引入程序集的各种方法,这一章节笔者将探究 C# 中使用反射的各种操作和代码实践。

1,反射的使用概述

1.1 什么是反射

《C# 7.0 本质论》中:

反射是指对程序集中的元数据进行检查的过程。

《C# 7.0 核心技术指南》中:

在运行时检查并使用元数据和编译代码的操作称为反射

Microsoft Docs :

反射提供描述程序集、模块和类型的对象。 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问其字段和属性。

1.2 反射可以做什么

《C# 7.0 本质论》、《C# 7.0 核心技术指南》、《Microsoft Docs》中,关于反射的作用,提纲整理如下:

  • 需要访问程序元数据中的特性时;
  • 检查和实例化程序集中的类型;
  • 在运行时构建新类型( Emit 技术);
  • 执行后期绑定,访问在运行时创建的类型上的方法;
  • 访问程序集中类型的元数据:

    其中包括像完整类型名和成员名这样的构造,以及对一个构造进行修饰的任何特性。·使用元数据在运行时动态调用类型的成员,而不是使用编译时绑定。
  • .NET通过 C# 语言提供的诸多服务(例如动态绑定、序列化、数据绑定和 Remoting)都是依托于元数据的:

    我们的应用程序可以充分地利用这些元数据,甚至可以通过自定义特性向元数据中添加信息。我们甚至可以通过 System.Reflection.Emit 命名空间中的类在运行时动态创建新的元数据和可执行IL(中间语言)指令。

1.3 Type 类

System.Type

表示类型声明:类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型。

Type 类型是反射技术的基础,反射所有操作都离不开 Type。

1.4 反射使用归类

C# 中,一个类型,可有以下元素组成:

类型名、构造函数/参数、方法/方法参数、字段、属性、基类型、继承接口等。

而我们使用反射技术时,一般关注以下的信息:

  • 类型的名称

    Type.Name
  • 类型是不是 public

    Type.IsPublic
  • 类型的基类型

    Type.BaseType
  • 类型支持哪些接口

    Type.GetInterfaces()
  • 类型在哪个程序集中定义

    Type.Assembly
  • 类型的属性、方法、字段

    Type.GetProperties()
    Type.GetMethods()
    Type.GetFields()
  • 修饰类型的特性

    Type.GetCustomAttributes()

1.4 Type 一些常用属性

            Type type = typeof(Program);
Console.WriteLine(type.Namespace);
Console.WriteLine(type.Name); // 类型名称
Console.WriteLine(type.FullName); // 类型的完全限定名 Assembly ass = type.Assembly;
Console.WriteLine(ass.FullName);

输出:

Mytest
Program
Mytest.Program
ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

下面,笔者将慢慢探究 C# 中关于反射的内容以及实践验证。

2,获取 Type

2.1 获取 Type 类型

获取 Type 主要有两种方法:

        Type type1 = typeof(MyClass);

        MyClass myClass = new MyClass();
Type type2 = myClass.GetType();

typeof() 静态方法,可以直接获取一个类型的 Type;

.GetType() 则是获取一个实例的类型;

两种方法想必各位以及司空见惯~

反射一般是编写代码时,很多情况不能明确下才使用,一般结合程序集来获取;

            Assembly ass = Assembly.LoadFrom(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Console.dll");

            // 获取当前控制台程序的程序集,并且获取 Console 这个类型
// 注意,要使用完全限定名
Type type = ass.GetType("System.Console");
Type[] types = ass.GetTypes();
Console.WriteLine("type: " + type.Name); // 获取 System.Console.dll 中的所有类型
foreach (var item in types)
{
Console.WriteLine("item: " + item.Name);
}
Console.ReadKey();

关于完全限定名,要根据实际情况填写。

2.2 数组 Type

获取数组的 Type ,这里有两种情况,一种是将类型生成类型的数组,另一种是本身就是数组类型;

例如说,

本身是 int 类型, 生成 int[] 数组的 Type 类型;

本身是 int[] 类型,生成 int[] 数组的 Type 类型;

生成数组 Type

前者通过实例的 MakeArrayType() 方法生成,示例如下

            // int 生成 int[]
Type typeArray_A = typeof(int).MakeArrayType();
// int 生成 int[,] 多维数组
Type typeArray_B = typeof(int).MakeArrayType(2); Console.WriteLine(typeArray_A.Name);
Console.WriteLine(typeArray_B.Name);
Console.ReadKey();

输出

Int32[]
Int32[,]

如果是交错数组 [][] 呢。。。怎么创建。。。别急。。。后面有。。。

获取数组 Type

如果一个类型本身就是数组呢?

            Type type = typeof(int[]);
Console.WriteLine(type);

不需要进行任何操作。。。

获取数组的元素类型、维数

假如有个数组 int[],我们要获取数组的元素类型 int,可以使用

            Type type = typeof(int[]);
Type type1 = type.GetElementType();
Console.WriteLine(type1.Name);

获取一个多维数组有维数

            Type type = typeof(int[,,,,,,,,,,]);
Console.WriteLine(type.GetArrayRank());

输出:11

            Type type = typeof(int[][][][][]);
Console.WriteLine(type.GetArrayRank());

输出:1

矩形数组(交错数组)

int[,] 这样的,称为多维数组;

int[][]这样的,称为矩形数组、交错数组、锯齿数组(称呼有点多)。

关于这方面的知识,可以参考笔者的另一篇文章:https://www.cnblogs.com/whuanle/p/9936047.html

Type 中,没有创建交错数组的方式,因为实际上,交错数组是 数组的数组,例如 (int[]) 的数组。

            // 先获取一个类型
Type arrayType = typeof(int[]);
Type _Array = arrayType.MakeArrayType();
Console.WriteLine(_Array.Name);

输出

Int32[][]

2.3 嵌套类型

嵌套类型的使用跟正常的类型一致,嵌套类型的完全限定名称由 {类型}+{嵌套类型} 组成,其它地方没有什么差异。

在 Program 创建一个类 MyClass

            Type type = typeof(Program.MyClass);
Console.WriteLine(type.Namespace);
Console.WriteLine(type.Name);
Console.WriteLine(type.FullName); Assembly ass = type.Assembly;
Console.WriteLine(ass.FullName);

输出:

Mytest
MyClass
Mytest.Program+MyClass
ConsoleApp4, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

完全限定名称:Mytest.Program+MyClass,由加号 + 连接。

2.4 泛型 Type

泛型信息

先看以下例子

        Type typeA = typeof(Dictionary<,>);
Type typeB = typeof(Dictionary<string, int>);
Type typeC = typeof(List<>);
Type typeD = typeof(List<string>);

然后打印 Name

            Console.WriteLine(typeA.Name);
Console.WriteLine(typeB.Name);
Console.WriteLine(typeC.Name);
Console.WriteLine(typeD.Name);

输出

Dictionary`2
Dictionary`2
List`1
List`1

打印 FullName

            Console.WriteLine(typeA.FullName);
Console.WriteLine(typeB.FullName);
Console.WriteLine(typeC.FullName);
Console.WriteLine(typeD.FullName);

输出

System.Collections.Generic.Dictionary`2

System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]

System.Collections.Generic.List`1

System.Collections.Generic.List`1[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]

Dictionary2 ,这个 `后表示此泛型类型有多少个泛型参数。

使用 .Name 看不出参数以及参数类型;FullName 可以看到完整的参数。

泛型相关

Type 中,与 泛型 有关的函数如下:

System.Type 成员名称 说明
IsGenericType 如果类型是泛型,则返回 true。
GetGenericArguments() 返回 Type 对象的数组,这些对象表示为构造类型提供的类型实参或泛型类型定义的类型形参。
GetGenericTypeDefinition() 返回当前构造类型的基础泛型类型定义。
GetGenericParameterConstraints() 返回表示当前泛型类型参数约束的 Type 对象的数组。
ContainsGenericParameters() 如果类型或任何其封闭类型或方法包含未提供特定类型的类型参数,则返回 true。
GenericParameterAttributes() 获取描述当前泛型类型参数的特殊约束的 GenericParameterAttributes 标志组合。
GenericParameterPosition() 对于表示类型参数的 Type 对象,获取类型参数在声明其类型参数的泛型类型定义或泛型方法定义的类型参数列表中的位置。
IsGenericParameter 获取一个值,该值指示当前 Type 是否表示泛型类型或方法定义中的类型参数。
IsGenericTypeDefinition 获取一个值,该值指示当前 Type 是否表示可以用来构造其他泛型类型的泛型类型定义。 如果该类型表示泛型类型的定义,则返回 true。
DeclaringMethod() 返回定义当前泛型类型参数的泛型方法,如果类型参数未由泛型方法定义,则返回 null。
MakeGenericType() 替代由当前泛型类型定义的类型参数组成的类型数组的元素,并返回表示结果构造类型的 Type 对象。
            Type typeA = typeof(Dictionary<,>);
Type typeB = typeof(Dictionary<string, int>);
Console.WriteLine(typeA.IsGenericMethodParameter +"|"+typeB.IsGenericMethodParameter);
Console.WriteLine(typeA.IsGenericParameter + "|" + typeB.IsGenericParameter);
Console.WriteLine(typeA.IsGenericType + "|" + typeB.IsGenericType);
Console.WriteLine(typeA.IsGenericTypeDefinition + "|" + typeB.IsGenericTypeDefinition);
Console.WriteLine(typeA.IsGenericTypeParameter + "|" + typeB.IsGenericTypeParameter);

输出:

False|False
False|False
True|True
True|False
False|False

老实说,除了 IsGenericType,其它的我都不懂什么意思。

GetGenericArguments() 可以获取泛型的参数类型;

            Type type = typeof(Dictionary<string, int>);
Type[] list = type.GetGenericArguments();
foreach (var item in list)
Console.WriteLine(item.Name);

输出

String
Int32

2.5 方法的参数和 ref / out

创建一个类型

        public class MyClass
{
public void Test(string str, ref string a, out string b)
{
b = "666";
Console.WriteLine(b);
}
}

获取方法的参数列表

            // 获取一个方法的参数列表
ParameterInfo[] paramList = typeof(MyClass).GetMethod(nameof(MyClass.Test)).GetParameters(); foreach (var item in paramList)
Console.WriteLine(item);

输出:

System.String str
System.String& a
System.String& b

如果参数类型后面有 & ,则代表是 ref 或 out 修饰的参数。

此文仅授权《NCC 开源社区》订阅号发布

C#反射与特性(二):探究反射的更多相关文章

  1. .NET基础拾遗(4)委托、事件、反射与特性

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理基础 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开 ...

  2. 十七、C# 反射、特性和动态编程

    反射.特性和动态编程   1.访问元数据 2.成员调用 3.泛型上的反射 4.自定义特性 5.特性构造器 6.具名参数 7.预定义特性 8.动态编程   特性(attribute)是在一个程序集中插入 ...

  3. C#根据反射和特性实现ORM映射实例分析

    本文实例讲述了C#根据反射和特性实现ORM 映射的方法.分享给大家供大家参考.具体如下: (一)关于反射 什么是反射? 反射就是在运行时,动态获取对象信息的方法.比如:运行时获得对象有哪些属性,方法, ...

  4. C#反射与特性(七):自定义特性以及应用

    目录 1,属性字段的赋值和读值 2,自定义特性和特性查找 2.1 特性规范和自定义特性 2.2 检索特性 3,设计一个数据验证工具 3.1 定义抽象验证特性类 3.2 实现多个自定义验证特性 3.3 ...

  5. C#反射与特性(九):全网最全-解析反射

    目录 1,判断类型 1.1 类和委托 1.2 值类型 1.3 接口 1.4 数组 2, 类型成员 2.1 类 2.2 委托 2.3 接口 [微信平台,此文仅授权<NCC 开源社区>订阅号发 ...

  6. 利用反射的特性将DataReader对象转化为List集合

    问题:将SqlDataReader对象转换为List<T>集合 思路: 1,利用反射的特性得到对应实体Model的公共属性 Type type = typeof(T); PropertyI ...

  7. C#图解教程 第二十四章 反射和特性

    反射和特性 元数据和反射Type 类获取Type对象什么是特性应用特性预定义的保留的特性 Obsolete(废弃)特性Conditional特性调用者信息特性DebuggerStepThrough 特 ...

  8. C#反射与特性使用简介

    本文是学习特性与反射的学习笔记,在介绍完特性和反射之后,会使用特性与反射实现一个简单的将DataTable转换为List的功能,水平有限,如有错误,还请大神不吝赐教. 1.      反射:什么是反射 ...

  9. .NET技术-1.0.使用反射、特性简化代码(验证Model类)

    使用反射.特性简化代码 参考项目:利用反射验证Model类/AssemblyVerification 假设现在有一个学生类(Student) /// <summary> /// 学生类 / ...

随机推荐

  1. CNN如何识别一幅图像中的物体

    让我们对卷积神经网络如何工作形成更好直观感受.我们先看下人怎样识别图片,然后再看 CNNs 如何用一个近似的方法来识别图片. 比如说,我们想把下面这张图片识别为金毛巡回犬.   一个需要被识别为金毛巡 ...

  2. IP应用加速技术详解:如何提升动静混合站点的访问速率?

    全站加速(DCDN)-IPA是阿里云自主研发四层加速产品,它基于TCP/UDP的私有协议提供加速服务,包括解决跨运营商网络不稳定.单线源站.突发流量.网络拥塞等诸多因素导致的延迟高.服务不稳定的问题, ...

  3. Oracle使用——varchar2() 和 char()关联查询 存在空格

    背景 表dbcontinfo 字段loanid,类型为varchar2(60) 表dbloanbal 字段loanid,类型为char(60) loanid字段实际长度为24位 问题 两张表dbloa ...

  4. SDO_RELATE和SDO_GEOM.RELATE

    SDO_RELATE需要事先建立索引,而SDO_GEOM.RELATE不用. SDO_RELATE返回True或False, SDO_GEOM.RELATE除可返回True或False外,还可返回关系 ...

  5. JS iFrame 加载慢怎么解决

    在项目中经常要动态添加iframe,然后再对添加的iframe进行相关操作,有时候会遇到iframe加载很慢什么原因呢,该如何解决呢?带着这个问题一起通过本文学习,寻找答案吧! aaa.html &l ...

  6. [转]Java多线程学习(总结很详细!!!)

    Java多线程学习(总结很详细!!!) 此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢? 本文主要讲java中多线程 ...

  7. npx cowsay 让动物说话~

      发现个好玩的东东, 忍不住想分享出来, 好可爱, 哈哈哈~~   node环境执行命令:    npm i cowsay -D npx cowsay hello! npx cowsay -f sh ...

  8. NLP --- 条件随机场CRF详解 重点 特征函数 转移矩阵

    上一节我们介绍了CRF的背景,本节开始进入CRF的正式的定义,简单来说条件随机场就是定义在隐马尔科夫过程的无向图模型,外加可观测符号X,这个X是整个可观测向量.而我们前面学习的HMM算法,默认可观测符 ...

  9. servicemix-3.2.1 内置的服务引擎和绑定组件

    服务引擎: servicemix-bean servicemix-camel servicemix-cxf-se servicemix-drools servicemix-eip servicemix ...

  10. 2018-8-10-C#-判断文件编码

    title author date CreateTime categories C# 判断文件编码 lindexi 2018-08-10 19:16:52 +0800 2018-2-13 17:23: ...