前面三篇文章,介绍了使用程序集、获取 Type 类型、使用 Type 类型获取成员信息。

通过前面的学习,我们大概了解到 Assembly、PropertyInfo、FieldInfo、ConstructorInfo、MethodInfo、ParameterInfo、EventInfo、MemberInfo 的存在和输出信息。

从本篇文章,将开始通过反射实例化类型,进行一系列的反射操作实践。

本篇文章,主要讲述实例化类型、实例化委托。

1,实例化类型

从类型(Type)创建实例对象的方式,有两种

  • Activator.CreateInstance() 方法 ,操作 类型 Type
  • ConstructorInfo.Invoke(),操作 构造函 ConstructorInfo

实例化一个类型时,首先考虑类型的构造函数。

1.1 Activator.CreateInstance()

首先,在 Microsoft Docs 中,这么定义:

使用与指定参数匹配程度最高的构造函数创建指定类型的实例。

这是什么意思呢?

我们来看一下 Activator.CreateInstance() 最常用的两个个重载。

object? CreateInstance(Type type);
object? CreateInstance(Type type, params object[] args);

args 就是实例化类型时,给构造函数传递的参数。因为使用的是 object ,最终实例化是使用到的 构造函数 是 区配程度 最高的。

好了,不扯了,我们来实践一下。

1.1.1 简单类型

            Type typeA = typeof(int);
object objA = Activator.CreateInstance(typeA);

通过上面的代码,我们可以很方便的实例化一个简单类型。

当然,你可以看到,创建后的类型是 object 。

那么,问题来了

反射后,少不得一顿装箱拆箱了。

目前来说,我们使用不了 int 的方法了,只能使用 object 。怎么办?

先留着后面再解决呗。

当然,可以直接使用 int ,那我还使用反射干嘛?

int i = 666;

这样不就行了?

如果需要在程序生成后,引用 dll 的代码,我们可以这样做

            Assembly ass = Assembly.LoadFrom(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Runtime.dll");
Type typeA = ass.GetType("System.Int32");
object objA = Activator.CreateInstance(typeA);

1.1.2 简单类型的构造函数

对于 int 这些简单类型,没有别的操作,直接实例化就行,这里例举 DateTime 类型,通过不同的参数,调用构造函数去实例化。

            Type typeA = typeof(DateTime);
object objA = Activator.CreateInstance(typeA,2020,1,5);

当然,如果无法找到合适的构造函数来实例化类型,则会弹出 System.MissingMethodException 异常。

1.1.3 object

让我们创建一个类型

        public MyClass(object a, object b)
{ } public MyClass(string a, string b)
{ }
public MyClass(string a, object b)
{ }

通过反射创建实例

            Type typeA = typeof(MyClass);
object objA = Activator.CreateInstance(typeA, 2020,666); Console.WriteLine(typeA.Name);

以上代码并不会报错。

原因有两个,① 类型转换至 object,会携带原类型的信息;② Activator.CreateInstance() 会寻找最优的构造函数。

所以上面创建实例化时,会调用 public MyClass(int a, int b)。没有符合的怎么办?那就调用最优解;

所以上面这点小意思,不会造成任何影响的。

对于简单类型,寻找过程如下

1,寻找相应类型的构造函数

Activator.CreateInstance(typeA, 2020,666),2020 是 typeo(int),666 是 typeof(int)。

最优是 public MyClass(int a, int b)

2,找不到的话,就找可以隐式转换的构造函数

例如 int -> long;

public MyClass(long a, long b)

3,如果没有隐式转换,则 object

public MyClass(object a, object b)

如果都没有符合条件的话,只能报错了;

验证一下

    public class MyClass
{
public MyClass(string a, string b) { }
public MyClass(int a, int b) { }
}
class Program
{
static void Main(string[] args)
{
Type typeA = typeof(MyClass);
long a = 666;
long b = 666; object objA = Activator.CreateInstance(typeA, a, b); Console.WriteLine(typeA.Name);
Console.ReadKey();
}
}

不出意外的话,上面代码会报错。

1.1.4 故意出错

    public class MyClass
{
public MyClass(string a = null) { }
public MyClass(StringBuilder a = null) { }
}
class Program
{
static void Main(string[] args)
{
Type typeA = typeof(MyClass); object objA = Activator.CreateInstance(typeA, null); Console.WriteLine(typeA.Name);
Console.ReadKey();
Console.ReadKey();
}
}

如无意外,上面的代码,执行后会报错。

因为当实例化时,参数为 null,有两个符合要求的构造函数。

其它情况下,根据 1.1.3 中,寻找构造函数的步骤,可以大致判断是否会出错。

1.1.5 Activator.CreateInstance() 性能

我们来通过正常的代码实例化一个类型,实声明并且赋值,共 1 千万次。

            Stopwatch time = new Stopwatch();
time.Start();
for (int i = 0; i < 1_000_0000; i++)
{
int a = 666;
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);
time.Reset();
time.Restart();
for (int i = 0; i < 1_000_0000; i++)
{
int a = 666;
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);

时间

24
23

使用反射

            Type typeA = typeof(int);
Stopwatch time = new Stopwatch();
time.Start(); for (int i = 0; i < 1_000_0000; i++)
{
object objA = Activator.CreateInstance(typeA);
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);
time.Reset();
time.Restart();
for (int i = 0; i < 1_000_0000; i++)
{
object objA = Activator.CreateInstance(typeA);
}
time.Stop();
Console.WriteLine(time.ElapsedMilliseconds);

时间

589
504

500 / 25 = 20,没错,性能相差了 20倍以上。

1.2 ConstructorInfo.Invoke()

ConstructorInfo.Invoke() 调用构造函数的限制性比Activator.CreateInstance() 高,并且是严格对应的。

1.1.4 中,故意出错的代码中,可以看到因为 null 时,有多个构造函数符合条件而导致程序报错。

使用 ConstructorInfo.Invoke() 创建实例进行测试。

    public class MyClass
{
public MyClass(string a = null) { Console.WriteLine(6666); }
public MyClass(StringBuilder a = null) { }
}
class Program
{
static void Main(string[] args)
{
// 通过唯一性获取构造函数
// 通过参数类型和数量,获取到唯一的构造函数
ConstructorInfo conStruct = typeof(MyClass).GetConstructor(new Type[] { typeof(string) }); // 传输参数值并且进行实例化
object objA = conStruct.Invoke(new object[] { null }); Console.ReadKey();
}
}

使用 typeof(MyClass).GetConstructor(new Type[] { typeof(string) }); 获取到类型的构造函数,然后使用 ConstructorInfo.Invoke() 实例化。

上面 GetConstructor() 的方法,重载定义如下

public ConstructorInfo? GetConstructor(Type[] types);

通过什么的方法,可以使用 public 构造函数实例化一个类型,如果想调用非 public 的构造函数呢?

可以使用 BindingFlags,这些后面再慢慢学习。

2,实例化委托

使用 Delegate.CreateDelegate() 方法实例化一个委托,使用 Delegate.DynamicInvoke() 调用委托并且传递参数。

使用形式

CreateDelegate(Type, Object, MethodInfo)

Type 是此委托类型,Object 、MethodInfo 是实例类型、方法。

有两种情况,一种是实例方法、一种是静态方法。

我们创建一个委托以及类型

    delegate int Test(int a, int b);
public class MyClass
{
public int A(int a, int b)
{
Console.WriteLine("A");
return a + b;
}
public static int B(int a, int b)
{
Console.WriteLine("B");
return a - b;
}
}

Main() 中 实验代码如下

            // 绑定实例方法
Delegate d1 = Delegate.CreateDelegate(typeof(Test), new MyClass(), "A"); // 绑定静态方法
Delegate d2 = Delegate.CreateDelegate(typeof(Test), typeof(MyClass), "B"); Console.WriteLine(d1.DynamicInvoke(333,333));
Console.WriteLine(d2.DynamicInvoke(999,333)); Console.ReadKey();

输出

A
666
B
666

3,实例化泛型类型

3.1 实例化泛型

实例化一个泛型类型时,可以按照实例化普通类型过程操作

            // 正常
Type type = typeof(List<int>);
object obj = Activator.CreateInstance(type); // 下面的会报错
Type _type = typeof(List<>);
object _obj = Activator.CreateInstance(_type);

使用 Activator.CreateInstance 方法实例化一个泛型类型时,必须是 已绑定类型参数 的泛型 Type。

List<int> 已绑定 √; List<> 未绑定 ×。

另外,通过 ConstructorInfo.Invoke() 实例化也是一样的。

    public class MyClass<T>
{
public MyClass(T a)
{
Console.WriteLine(a);
}
}
class Program
{
static void Main(string[] args)
{
// 正常
ConstructorInfo type = typeof(MyClass<int>).GetConstructor(new Type[] { typeof(int) });
object obj = type.Invoke(new object[] { 666 }); Console.ReadKey();
}
}

3.2 构造封闭泛型类型以及反转

3.2.1 构造封闭构造函数

有时候,传递过来的恰恰是 List<> 呢?

使用 Type.MakeGenericType(Type)

我们可以这样多一步,将未绑定类型参数的泛型 Type,转为封闭的 泛型 Type。

            Type type = typeof(List<>);

            // 构建泛型 Type
Type _type = type.MakeGenericType(typeof(int));
object _obj = Activator.CreateInstance(_type);

3.2.2 去除泛型类型的参数类型绑定

使用 Type.GetGenericTypeDefinition() 方法可以去除一个已绑定参数类型的泛型类型的参数类型。

            Type type = typeof(List<int>);
Console.WriteLine(type.FullName);
// 构建泛型 Type
Type _type = type.GetGenericTypeDefinition();
Console.WriteLine(_type.FullName);

输出

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

List<int> 变成了 List<>

3.2.3 实践一下

上面介绍了泛型类型的实例化和两个关于参数类型的使用,下面来实践一下

        static void Main(string[] args)
{
Type typeA = typeof(Console);
Type typeB = typeof(List<>);
Type typeC = typeof(List<int>); 去除泛型类型绑定的参数类型(typeA);
去除泛型类型绑定的参数类型(typeB);
去除泛型类型绑定的参数类型(typeC);
Console.ReadKey();
} /// <summary>
/// 将 List<T> 转为 List<>
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static (bool, Type) 去除泛型类型绑定的参数类型(Type type)
{
// 检查是否泛型类型
if (type.IsGenericType == false)
{
Console.WriteLine("此类型不是泛型类型");
return (false, type);
} // 检查是否是未绑定参数类型的泛型类型
if (type.IsGenericTypeDefinition)
{
Console.WriteLine("本来就不需要处理");
return (true, type);
} Type _type = type.GetGenericTypeDefinition();
Console.WriteLine("处理完毕");
return (true, _type);
}
}

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

C#反射与特性(四):实例化类型的更多相关文章

  1. C#反射与特性(五):类型成员操作

    目录 1,MemberInfo 1.1 练习-获取类型的成员以及输出信息 1.2 MemberType 枚举 1.3 MemberInfo 获取成员方法并且调用 1.4 获取继承中方法的信息(Decl ...

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

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

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

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

  4. C#反射与特性(六):设计一个仿ASP.NETCore依赖注入Web

    目录 1,编写依赖注入框架 1.1 路由索引 1.2 依赖实例化 1.3 实例化类型.依赖注入.调用方法 2,编写控制器和参数类型 2.1 编写类型 2.2 实现控制器 3,实现低配山寨 ASP.NE ...

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

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

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

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

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

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

  8. C# 反射与特性(十):EMIT 构建代码

    目录 构建代码 1,程序集(Assembly) 2,模块(Module) 3,类型(Type) 4,DynamicMethod 定义方法与添加 IL 前面,本系列一共写了 九 篇关于反射和特性相关的文 ...

  9. C#中通过反射方法获取控件类型和名称

    这个方法是简单的也是神奇的. 有木有想过,将自己项目中的所有类型,包括自定义类型的命名空间和名称全部获取出来? 有木有想过,有一种简便的方法可以自动化管理项目中的控件和窗体? 有木有想过... 首先, ...

随机推荐

  1. 有关常用的UI组件

    前段时间想看看大家都用什么UI组件,下面是在网上搜集的,记录在这里方便自己使用和其他小伙伴使用 1:WeUI 小程序–使用教程 https://weui.io/ 官方介绍:WeUI 是一套同微信原生视 ...

  2. linux权限说明

    Linux 中的权限 这里我拿一个tomcat目录举例说明.首先我们在linux系统中执行命令:ls -l 我们拿出其中一个说明-rwxr-xr-x,在Linux 系统中权限是区分用户的,即用户.组用 ...

  3. 07Shell数组

    Shell 数组变量 普通数组:只能使用整数作为数组索引 关联数组:可以使用字符串作为数组索引 普通数组 定义数组 方法1: 一次赋一个值 数组名[索引]=变量值 示例 # array1[0]=pea ...

  4. 机器学习之路--Pandas

    Pandas 是对numpy的封装 Pandas 核心结构DataFrame 近似看出矩阵结构panda字符型叫object dataframe其中一行或者一列叫series dataframe 里面 ...

  5. 1067 试密码 (20分)C语言

    当你试图登录某个系统却忘了密码时,系统一般只会允许你尝试有限多次,当超出允许次数时,账号就会被锁死.本题就请你实现这个小功能. 输入格式: 输入在第一行给出一个密码(长度不超过 20 的.不包含空格. ...

  6. .NET Core 3.0 System.Text.Json 和 Newtonsoft.Json 行为不一致问题及解决办法

    行为不一致 .NET Core 3.0 新出了个内置的 JSON 库, 全名叫做尼古拉斯 System.Text.Json - 性能更高占用内存更少这都不是事... 对我来说, 很多或大或小的项目能少 ...

  7. React Native 性能优化指南【全网最全,值得收藏】

    2020 年谈 React Native,在日新月异的前端圈,可能算比较另类了.文章动笔之前我也犹豫过,但是想到写技术文章又不是赶时髦,啥新潮写啥,所以还是动笔写了这篇 React Native 性能 ...

  8. Mysql备份与恢复(2)---逻辑备份

    数据库及时备份可以帮助我们在数据库出现异常宕机时及时的使用备份数据进行恢复工作,将因为数据库宕机产生的影响降低到最小.上一篇针对使用xtrabackup工具进行物理备份和数据恢复做了一个详细讲解,本篇 ...

  9. docker+mysql 更改配置后重启不了的解决方案

    docker+mysql 更改配置后重启不了的解决方案 前提:在最近的项目中,决定将项目改造成数据库读写分离的架构,于是擅自更改生产环境的数据库的配置文件my.cnf,由于我是用docker进行部署的 ...

  10. wpf 画五角星函数

    public void ABC() { var canvas = new Canvas(); Content = canvas; var points = new List<Point>( ...