首页 > 其他分享 >反射

反射

时间:2024-05-24 10:09:53浏览次数:7  
标签:反射 int 程序 System using public

反射

反射的概念

  • 程序是用来处理数据的,而程序本身也是数据,有关程序及其类型的数据被称为 MetaData ,保存在程序集中。程序在运行时,可以查看其他程序集或其本身 Metadata,一个运行的程序查看本身的元数据或其他程序集的元数据的行为称为反射(Reflection)

  • 通过反射,可以在运行时获得某个类型的各种信息,包括方法、属性、事件、及构造函数等,还可以获得每个成员的名称等信息

反射的特点

  • 在程序运行时,动态创建对象、调用方法、设置属性和激发事件,而不是在编译时完成

反射的应用

  • 在VS的智能提示、使用MSIL反汇编工具查看IL代码都用的时反射技术。

  • Java开发工具Eclipse中插件使用,也都是反射技术

开发中的应用

  • 系统需要基于插件开发的时候,必须要用反射

  • 在简单工厂和抽象工厂设计模式中讲使用反射技术

  • 使用反射一般都要配合接口使用

  • 反射技术使得系统性能一定程度降低,除非必要情况,反射不宜过多使用

什么是Type?

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

什么是程序集?

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

反射、DLL、IL、Metadata、Assembly

C# 编写的源代码被编译成符合 CLR 规范的中间语言 (IL)。 IL 代码和资源(如位图和字符串)存储在扩展名通常为 .dll or .exe 的程序集中。程序集包含一个介绍程序集的类型、版本和区域性的清单,这些描述性信息称为 Metadata(元数据),而反射就需要在 Assembly 中找到这些 Metadata 然后进行对象实例化、调用方法、读取属性等。


1. 程序集加载

alt text

ReflectionTest类

    public class ReflectionTest
  {
       public ReflectionTest()
      {
           Console.WriteLine("This is a non-parametric constructor");
      }
       public ReflectionTest(string name)
      {
           Console.WriteLine($"This is a parametric construction , parameter:{name}");
      }

       //private ReflectionTest()
       //{
       //   Console.WriteLine("This is a private constructor");
       //}
  }
  1. Load 一般用来加载同一文件下的其他程序集 无需加dll后缀,不可以指定路径 var assembly = Assembly.Load("TestDll");

  2. LoadFile 一般用来加载不再同一文件下的其他程序集 必须写上完全限定路径 var assm = Assembly.LoadFile(@"C:\Users\43789\Documents\LINQPad Queries\反射\TestDll.dll");

  3. LoadFrom 一般用来加载不再同一文件下的其他程序集 var assm2 = Assembly.LoadFrom(@"C:\Users\43789\Documents\LINQPad Queries\反射\TestDll.dll"); 或者放在当Dll文件在启动程序目录时 var assm3 = Assembly.LoadFrom("TestDll.dll");

2. 获取程序集(dll)中的指定类型

Type type = assm2.GetType("TestDll.ReflectionTest");

3. 实例化对象


var assm = Assembly.LoadFrom(@"C:\Users\43789\Documents\LINQPad Queries\反射\TestDll.dll");

//获取类型
Type type = assm.GetType("TestDll.ReflectionTest");

//通过类型创建实例,返回值是object类型
object objTest = Activator.CreateInstance(type);//调用无参的构造函数

//调用有参数的构造函数 参数以object数组传递进来
object objTest2 = Activator.CreateInstance(type, new object[]{"Test"});


Type typeTest = typeof(Test);
//调用私有化的构造函数 第二个参数 bool nonPublic 选择true
object obj = Activator.CreateInstance(typeTest, true);

public class Test
{
private Test()
{
   Console.WriteLine("This is a private constructor");
}
}

4. 获取构造函数和构造函数的参数

var assm = Assembly.LoadFrom(@"C:\Users\43789\Documents\LINQPad Queries\反射\TestDll.dll");

// 获取当前dll文件里所有的类类型
assm.GetTypes()
.ToList()
.ForEach(t=> t.Dump());


//获取ReflectionTest类里所有的构造函数并打印
assm.GetType("TestDll.ReflectionTest")
.GetConstructors()
.ToList()
.ForEach(ctor=>ctor.Dump());

//获取所有构造函数里的参数
foreach (var ctor in assm.GetType("TestDll.ReflectionTest").GetConstructors())
{
foreach (var pi in ctor.GetParameters())
pi.Dump();
}

assm
.GetType("TestDll.ReflectionTest")
.GetConstructors()
.ToList()
.ForEach(ctor => ctor.GetParameters().ToList().ForEach(pi => pi.Dump()));

5.反射调用方法

Test类

public class Test
{
   public void Foo1()
  {
       Console.WriteLine($"这是一个普通方法 方法名: {nameof(Foo1)}" );
  }

   public void Foo2(int i)
  {
       Console.WriteLine($"这是一个有参数的方法 方法名: {nameof(Foo1)}, 参数是: {i.ToString()}");
  }

   public int Add(int a, int b)
  {
       Console.WriteLine($"调用有返回值的方法,方法名:{nameof(Add)} 接收返回值: {a + b}");
       return a + b;
  }

   public static void StaticFoo()
  {
       Console.WriteLine($"这是一个静态方法 方法名: {nameof(StaticFoo)}");
  }

   private void PrivateFoo()
  {
       Console.WriteLine($"这是一个私有方法 方法名: {nameof(PrivateFoo)}");
  }

   public void GenericFoo1<T1,T2>(T1 t1, T2 t2)
  {
       Console.WriteLine($"这是一个泛型方法 方法名:{nameof(GenericFoo1)}, 参数类型 {typeof(T1)},{typeof(T2)},参数 {t1},{t2}");
  }
}

实现方法


var assm = Assembly.LoadFrom(@"C:\Users\43789\Documents\LINQPad Queries\反射\TestDll.dll");
Type testType = assm.GetType("TestDll.Test")
// 通过方法名获取方法
var foo1 = testType.GetMethod("Foo1");
object objTest = Activator.CreateInstance(testType);

//调用方法,需要两个参数,一个实例对象,一个方法参数
foo1.Invoke(objTest, null); //无参数可以选择null
foo1.Invoke(objTest, new object[] {});//或者传入空数组

//调用有参数的方法
var foo2 = testType.GetMethod("Foo2");
foo2.Invoke(objTest, new object[] {5});

// 调用有返回值的方法
var add = testType.GetMethod("Add");
var res = add.Invoke(objTest, new object[] { 3, 5 });
$"返回值{res}".Dump();

// 调用静态方法
var staticFoo = testType.GetMethod("StaticFoo");
// 因为静态方法不需要创建类的实例,所以调用的时候第一个参数可以填null
staticFoo.Invoke(null,null);

// 调用私有方法
// 因为方法是私有的且是实例方法 所以第二个参数要选择BindingFlags.NonPublic 和 BindingFlags.Instance
var privateFoo = testType.GetMethod("PrivateFoo", BindingFlags.NonPublic| BindingFlags.Instance);
privateFoo.Invoke(objTest,null);

// 调用泛型方法
var genericFoo = testType.GetMethod("GenericFoo1");
// 设定泛型方法的参数并返回一个新的方法 参数用类型数组设置
var newFoo = genericFoo.MakeGenericMethod(new Type[] { typeof(int), typeof(string) });
newFoo.Invoke(objTest, new object[] {5,"str"});

6.反射调用泛型类

GenericClass

 public class GenericClass<T>
{
    public void GenericFoo<S>(S s1)
    {
        Console.WriteLine($"泛型类{nameof(GenericClass<T>)}+泛型方法{nameof(GenericFoo)}调用 泛型类类型:{typeof(T)}, 泛型方法参数类型:{typeof(S)}, 泛型方法传入参数{s1}");
    }
}

实现方法

var assm = Assembly.LoadFrom(@"C:\Users\43789\Documents\LINQPad Queries\反射\TestDll.dll");

//assm.GetType("xxx`n"); n代表泛型参数的个数     //创建泛型的类型,传入泛型类型string
Type t = assm.GetType("TestDll.GenericClass`1").MakeGenericType(new Type[] {typeof(double)});
t.Dump();
//泛型方法
var genericFoo = t.GetMethod("GenericFoo").MakeGenericMethod(new Type[]{typeof(string)});
genericFoo.Dump();
//创建实例
object instance = Activator.CreateInstance(t);
instance.Dump();
//调用
genericFoo.Invoke(instance, new object[] {"123"});

7. 反射调用属性和字段

PropertyClass

    public class PropertyClass
  {
       public int Id { get; set; }
       public string Name { get; set; }
       public string Phone { get; set; }
       public int id;
       public string name;
       public string phone;
  }

实现方法

Assembly assembly = Assembly.LoadFrom(@"C:\Users\43789\Documents\LINQPad Queries\反射\TestDll.dll");

Type type = assembly.GetType("TestDll.PropertyClass");

object instance = Activator.CreateInstance(type);

foreach (var prop in type.GetProperties())
{
if (prop.Name == "Id")
prop.SetValue(instance, 1);
if (prop.Name == "Name")
prop.SetValue(instance, "张三");
Console.WriteLine(prop.GetValue(instance));
}

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));
}
}

8.反射的简单应用

// 下面对 entity 承载的数据进行了实例化,并且依次赋值个了 entityDto,这种是常规写法。
Product product = new Product
{
ID = 1,
Name = "张三"
};

ProductDto productDto = new ProductDto();
productDto.ID = product.ID;
productDto.Name = product.Name;

// 如果字段多了呢,还需要手动依次赋值吗?
// 我们可以使用反射,对 entity entityDto 之间进行自动赋值,提高开发效率。
Product product1 = new Product
{
ID = 1,
Name = "张三"
};
// 获取Product类型
Type productType = typeof(Product);
Type productDtoType = typeof(ProductDto);

// 根据类型创建实例
object productDto1 = Activator.CreateInstance(productDtoType);

foreach (var prop in productDtoType.GetProperties())
{
// 依次拿取 dto 属性名称,在 Product Type 查找,并且从 Product Instance 获取值
object val = productType.GetProperty(prop.Name).GetValue(product1);
// ProductDto Instance Set Propertie Val
prop.SetValue(productDto1, val);
}

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; }
}

9. 使用反射的方式实现计算器

窗体实现

接口模块

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ICal
{
public interface ICalculator
{
/// <summary>
/// 加法接口
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
int Add(int a, int b);
/// <summary>
/// 减法接口
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
int Sub(int a, int b);

}
}

算法模块

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using ICal;//添加接口程序集引用

namespace CalDll
{
public class Calculator : ICal.ICalculator
{
//实现接口的加法
public int Add(int a, int b) => a + b;
//实现接口的减法
public int Sub(int a, int b) => a - b;

}
}

窗体实现

窗体实现

窗体类

using System;
using System.Windows.Forms;

using System.Reflection;//引入反射命名空间
using ICal;// 添加ICal 接口引用
// 不需要添加CalDll的引用

namespace CalculatorByReflection
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void btnAdd_Click(object sender, EventArgs e)
{
//从本地加载程序集[CalDLL.dll],从此程序集中找到指定的类型然后使用系统激活器创建它的实例
//Assembly 表示程序集
//LoadFrom 加载程序集 参数要写上程序集的具体名称 当前是从CalDll程序集的类来实现对象的具体方法
//CreateInstance 创建类的实例 参数是类的完全限定名 即命名空间+类名
//CreateInstance 返回的是object类型 需要加上强制转换
//所谓反射就是从你要创建类所在的程序集和类的完全限定名所表示
ICalculator calc = (ICalculator)Assembly.LoadFrom("CalDll.Dll").CreateInstance("CalDll.Calculator");

lblSum.Text = calc.Add(Convert.ToInt32(txtNum1), Convert.ToInt32(txtNum2)).ToString();


}
}
}

接口类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ICal
{
public interface ICalculator
{
/// <summary>
/// 加法接口
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
int Add(int a, int b);
/// <summary>
/// 减法接口
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
int Sub(int a, int b);

}
}

接口实现类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using ICal;//添加接口程序集引用

namespace CalDll
{
public class Calculator : ICal.ICalculator
{
//实现接口的加法
public int Add(int a, int b) => a + b;
//实现接口的减法
public int Sub(int a, int b) => a - b;

}
}

启动程序后如果抛文件未找到异常 alt text

需要手动复制CalDll程序集的dll文件到主程序debug目录 alt text


  • Assembly 表示程序集

  • LoadFrom 加载程序集 参数要写上程序集的具体名称 当前是从CalDll程序集的类来实现对象的具体方法

  • CreateInstance 创建类的实例 参数是类的完全限定名 即命名空间+类名

使用反射后,不需要添加CalDll的引用,降低模块之间的耦合


10.使用反射改进简单工厂

接口类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleFactoryByReflectionPro
{
public interface IReport
{
void Report();
}
}

实现接口类

using System.Windows.Forms;
// ExcelReport
namespace SimpleFactoryByReflectionPro
{
public class ExcelReport : IReport
{
public void Report()
{
MessageBox.Show("...正在使用Excel打印报表");
}
}
}


using System.Windows.Forms;
// WordReport
namespace SimpleFactoryByReflectionPro
{
internal class WordReport : IReport
{
public void Report()
{
MessageBox.Show("...正在使用Word打印报表");
}
}
}

工厂类

using System.Configuration;//添加引用
using System.Reflection;

namespace SimpleFactoryByReflectionPro
{
public static class Factory
{
// 1. 读取配置文件
static string reportType = ConfigurationManager.AppSettings["ReportType"].ToString();
// 2.使用反射创建实现接口类的对象并以接口类型返回
// 通过给定程序集[SimpleFactoryByReflectionPro],从此程序集中找到指定的类型然后使用系统激活器创建它的实例
// 即创建SimpleFactoryByReflectionPro.ExcelReport类对象
public static IReport GetReport()
{
return (IReport)Assembly.Load("SimpleFactoryByReflectionPro").CreateInstance("SimpleFactoryByReflectionPro." + reportType);
}
}
}

使用反射改进的优点 alt text

使用时不需要修改代码

 

 

 

标签:反射,int,程序,System,using,public
From: https://www.cnblogs.com/Honsen/p/18210033

相关文章

  • [Java]反射
    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)https://www.cnblogs.com/cnb-yuchen/p/17960654出自【进步*于辰的博客】参考笔记二,P75.3;笔记三,P15.2、P43.2、P44.2、P64.3、P69.1。1、什么是“反射”?关于类加载,详述可查阅博文《[Java]知识点》中的【类加载......
  • net 泛型反射入门使用
    目录结构 定义泛型反射ToModel.cs文件usingSystem;usingSystem.Collections.Generic;usingSystem.Data;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceAppraisal_System.Utility{//泛型反射publicstaticclassToMod......
  • 揭秘Java反射:如何轻松获取类的属性及父类属性
    哈喽,大家好,我是木头左!相信很多小伙伴在学习Java的过程中,都曾经遇到过需要动态地获取类的属性和方法的场景。而Java反射正是解决这个问题的利器。那么,如何使用Java反射来获取类的属性及父类的属性呢?一、Java反射简介要了解一下Java反射是什么。简单来说,Java反射就是运行时能够......
  • Python教程: 反射及常用的几种方法
    反射就是通过字符串映射或修改程序运行时的状态、属性、方法有四个常用方法:hasattr(obj,name_str)判断一个obj对象是否有对应name_str的方法getattr(obj,name_str)根据字符串name_str获取Obj对象中对应方法的内存地址setattr(obj,key,value)为对象Obj新增或修改属性......
  • .NET中特性+反射 实现数据校验
    .NET中特性+反射实现数据校验在.NET中,我们可以使用特性+反射来实现数据校验。特性是一种用于为程序中的代码添加元数据的机制。元数据是与程序中的代码相关联的数据,但不直接成为代码的一部分。通过特性,我们可以为类、方法、属性等添加额外的信息,这些信息可以在运行时通过反射获取......
  • .NET 反射
    .NET中的反射反射是什么?反射(Reflection)是.NET框架提供的一种强大的机制,它允许程序在运行时查询和操作对象的类型信息。通过反射,我们能够获取类型的属性、方法、构造函数等信息,甚至可以动态地创建类型实例和调用方法。反射是.NET框架中实现诸如序列化、反序列化、动态代理、依赖......
  • C#反射
    目录C#反射概述语法应用场景总结引用C#反射概述C#反射(Reflection)是一种强大的机制,它允许程序在运行时访问和操作.NET程序集中的类型和成员。获取程序集、模块和类型成员信息,三者关系介绍请查看。语法反射的核心概念是Type对象。Type对象表示一个.NET类型,并提供用......
  • java中的反射
    java中的反射能够分析信息的能力叫反射目录java中的反射1.获取类的方法2.类对象常用功能一、构造方法获取指定的公共构造器获取指定的所有构造器(公共+非公共)获取所有的公共构造方法获取所有的构造方法(公共+非公共)二、成员变量获取指定公共成员变量获取指定所有成员变量(公共+非......
  • Python进阶之反射
    【一】什么是反射反射是一种程序可以访问、检测和修改其本身状态或行为的能力在Python中,反射主要指通过字符串的形式操作对象的属性python中的一切事物都是对象,都可以使用反射【二】反射方法getattr(object,key):获取对象的属性值,如果属性不存在,可提供默认值hasattr(ob......
  • 抽象,多态,反射,内置方法
    Ⅰ抽象【一】什么是抽象将某几个具体的生物,根据特征总结成一个类,逐层向上总结#例如:#唐老鸭肉鸭北京烤鸭--->鸭子#北极熊黑熊-->熊#猫老虎-->猫科#鸭子熊猫科-->动物【二】什么是继承与抽象相反,自上而下解包#例如:#动物--->熊--->黑熊class......