C#学习,反射

目录

C#学习

.NET的体系结构

二次编译

反射

什么是反射?

什么是Type?

什么是程序集?

反射API:

一,程序集

1, Load

2,LoadFrom

3,LoadFile

二,类型实例

1,无参构造

2,有参构造

3,私有构造

4,泛型类

三,方法调用

1,普通方法

2,静态方法

3,私有方法

4,泛型方法

5,应用

四,属性与字段

1,属性读写

2,字段读写

3,应用

反射优缺点

优点

缺点

反射的价值是什么


C#学习

定义:面向对象,面向组件,类型安全,可以使用C#生成在.NET上运行的应用程序。

C#提供了语言构造来支持以上的定义,所以可以用来创建和使用软件组件。

.NET的体系结构

C#在.NET上运行,.NET叫做,公共语言进行时(CLR)的虚拟执行系统和一组类库.CLR 是 Microsoft 对公共语言基础结构 (CLI) 国际标准的实现。 CLI 是创建执行和开发环境的基础,语言和库可以在其中无缝地协同工作。

用 C# 编写的源代码被编译成符合 CLI 规范的中间语言 (IL)。 IL 代码和资源(如位图和字符串)存储在扩展名通常为 .dll 的程序集中。 程序集包含一个介绍程序集的类型、版本和区域性的清单(metadata)。

执行 C# 程序时,程序集将加载到 CLR。 CLR 会直接执行实时编译,将 IL 代码转换成本机指令。 CLR 可提供其他与自动垃圾回收、异常处理和资源管理相关的服务。

二次编译

目的:一次编译,多平台使用,加一层中间层,更加灵活

由于系统很多,Mac,Win,Linux,其中Win还有32和64位的,为了做到一次编译,多平台运行,就需要二次编译,C#源代码被编译成中间语言,存储在扩展名通常为 .dll 的程序集中, 程序集包含一个介绍程序集的类型、版本和区域性的清单(metadata)。

这样在不同的平台,同一份编译的中间语言,通过 CLR 转换为不同平台可执行机器指令,在不同平台执行。

反射

什么是反射?

程序是用来处理数据的,但是程序本身也是由数据组成的,有关程序及其类型的数据,元数据(Metadata),保存在程序集之中,程序在运行中,可以查看其他的程序集和自身的Metadata,一个运行的程序查看本身的元数据或其他程序集的元数据的行为称为反射。

什么是Type?

对于程序的类型,CLR都会创建一个包含这个类型的Type对象,程序中每遇到一个类型都会关联到独立的Type的对象,不管创建的类有多少实例,只有一个Type类的对象会关联到这些所有的实例。

什么是程序集?

程序集是一个可以寄宿于 CLR 中的、拥有版本号的、自解释、可配置的二进制文件,程序集的扩展名为 exe 或 dll。程序集是存放类型的集合,通过程序集可以获取程序集内所有的类型信息

反射API:

一,程序集

如果没有加载依赖,调用到使用依赖的对象时就会报错。

1, Load

XXX:dll 名称无后缀 从当前目录加载 dll。开发环境 Bin ,发布程序是入口程序集文件当前目录。

Assembly assembly = Assembly.Load("XXXX");
2,LoadFrom

XXX:已知程序集的文件名和路径,会自动加载程序集的依赖程序集。

Assembly assembly = Assembly.LoadFrom("XXXX");
3,LoadFile

XXX:完整的dll路径加载不会出错,不会加载目标程序集所引用和依赖的其他程序集,需要自己控制并显示加载所有依赖的程序集,如果没有依赖项,使用的时候会错。

Assembly assembly = Assembly.LoadFile("XXXX");

二,类型实例

1,无参构造

在获取Assembly后获取Type,根据Type创建实例,是Object类型的,为了编译器可以通过需要进行类型转换。

Type type = assembly.GetType("XXX.XXX");
object obj = (XXX)Activator.CreateInstance(type);
2,有参构造

在创建实例的时候,需要通过new object[]参数进行重载,此方法会根据 new object[] 里面的类型自动进行匹配构造函数

object obj = (XXX)Activator.CreateInstance(type, new object[] { "124", 123 });
3,私有构造

单例模式,在代码里面为了避免外面进行实例化,在实现时都是以 private 修饰符对构造函数进行修饰,使其无法在外部进行实例化进行调用,但反射可以破坏这个规则。重点在 CreateInstance 方法,第二个参数 true

Assembly assembly = Assembly.LoadFrom("XXXX");
Type type = assembly.GetType("XXX.XXX");
object obj = Activator.CreateInstance(type,true);
4,泛型类

~n个占位符就代表几个泛型,MakeGenericType 定义泛型类型,传入 Type 数组即可

Assembly assembly = Assembly.LoadFrom("XXXX");
Type type = assembly.GetType("XXX~n");
Type typeNew = type.MakeGenericType(new Type[] { typeof(int), typeof(XXX),typeof(string)  });
object obj = Activator.CreateInstance(typeNew, new object[] { "124", 123 });

三,方法调用

1,普通方法
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// 类型名称
object instance = Activator.CreateInstance(type); // 根据 type 实例对象
MethodInfo method = type.GetMethod("XXX");// 方法名称
method.Invoke(instance,new object[] { "方法参数1", "方法参数2" });
2,静态方法

静态成员,Invoke 无需传入实例对象,因为静态成员在类里面,只有一份,确定了 Type 后就已经有了其静态成员。

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// 类型名称
MethodInfo method = type.GetMethod("XXX");// 方法名称
method.Invoke(null,new object[] { "方法参数1", "方法参数2" });
3,私有方法

私有方法在面向对象编程语言是不可以被外部调用的,但反射可以破坏这个规则调用私有方法,重点在 GetMethod 方法第二个参数为 BindingFlags.Instance|BindingFlags.NonPublic。

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// 类型名称
object instance = Activator.CreateInstance(type); // 根据 type 实例对象
MethodInfo method = type.GetMethod("XXX",BindingFlags.Instance|BindingFlags.NonPublic);// 方法名称
method.Invoke(instance, new object[] { "方法参数1", "方法参数2" });
4,泛型方法

无论是泛型类还是泛型方法,都需要通过 MakeGenericXXXX 方法指定泛型的类型,需要注意的是泛型类在加载类型时需要占位符,而泛型方法不需要。

Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX~2");// 类型名称,~2 占位符,代表几个泛型
Type typeNew = type.MakeGenericType(new Type[] { typeof(string), typeof(int) });
object instance = Activator.CreateInstance(typeNew); // 根据 type 实例对象
MethodInfo method = type.GetMethod("XXX");// 方法名称
MethodInfo methodNew = method.MakeGenericMethod(new Type[] { typeof(int) });
methodNew.Invoke(instance, new object[] { "方法参数1", "方法参数2" });
5,应用

MVC就是使用的反射机制,在程序启动时,会扫描 controller 类型的类,会将其 Type 缓存起来,当有请求过来时,就会到缓存中找到对于的 Type 反射进行实例化并调用其方法(也就是 action)。说到这 mvc 的 filter 也就是在 调用方法前后加点料(反射 invoke 方法前后)。

四,属性与字段

1,属性读写
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX.XXX");// 类型名称
object instance = Activator.CreateInstance(type); //实例对象
foreach (var prop in type.GetProperties())
{
    if (prop.Name.Equals("Id"))
    {
        prop.SetValue(instance, 1);
        Console.WriteLine(prop.GetValue(instance));
    }
    else if (prop.Name.Equals("Name"))
    {
        prop.SetValue(instance, "张三");
        Console.WriteLine(prop.GetValue(instance));
    }
}
2,字段读写
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX.XXX");// 类型名称
object instance = Activator.CreateInstance(type); //实例对象
foreach (var field in type.GetFields())
{
    if (field.Name.Equals("id"))
    {
        field.SetValue(instance, 1);
        Console.WriteLine(field.GetValue(instance));
    }
    else if (field.Name.Equals("name"))
    {
        field.SetValue(instance, "张三");
        Console.WriteLine(field.GetValue(instance));
    }
}
3,应用

有一个 entity 与 entityDto:

public class Product
{
    public int ID { get; set; }
    public String Name { get; set; }
}
public class ProductDto
{
    public int ID { get; set; }
    public String Name { get; set; }
}

然后对 entity 承载的数据进行了实例化,并且依次赋值个了 entityDto

Product product = new Product()
{
    ID = 1,
    Name = "张三"
};
​
ProductDto productDto = new ProductDto();
productDto.ID = product.ID;
productDto.Name = product.Name;

对于上面的情况,还算简单,但是如果字段过多,手动赋值要花费的时间就大大增加,于是我们可以采用反射机制来提高效率,自动赋值,实际应用时,我们可以使用T来进行封装:

Product product = new Product()
{
    ID = 1,
    Name = "张三"
};
Type productType = typeof(Product); // Product Type
Type productDtoType = typeof(ProductDto);// ProductDto Type
object productDto = Activator.CreateInstance(productDtoType); // ProductDto Instance
foreach (var prop in productDtoType.GetProperties())
{
    // 依次拿取 dto 属性名称,在 Product Type 查找,并且从 Product Instance 获取值
    object val = productType.GetProperty(prop.Name).GetValue(product);
    // ProductDto Instance Set Propertie Val
    prop.SetValue(productDto,val);
}

反射优缺点

优点

动态:反射就两个字动态,就像 MVC 就是将方法的调用动态化了。数据库或者封装的处理业务逻辑的算法等,可以使用配置文件进行动态切换使用。

缺点
  • coding 复杂:面向对象静态编码,一两行的代码反射得写个四五行。

  • 避开编译器检查:平时写代码,我们写错了,编译的时候会 error 提示我们,如果没有编译器写得代码不知道错多少。但在反射里面,编译器对类的操作不会进行检查,这也是没有办法检查,因为反射是动态的运行时的,不像普通编码是静态的。

反射的价值是什么

反射最直观的区别是,由已有的固定类型,转化为了字符串操作,且不需要在项目中进行引用(反射是动态的,依赖的是字符串)。因为依赖的是字符串,我们的程序才可配置化、才可易扩展,包括平时的框架开发都在大量使用反射(MVC、IOC、ORM等)。

学习参考自:菜鸟厚非

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