C#反射(Reflection)详解及于java反射的对比

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: C#反射(Reflection)详解及于java反射的对比

大家好,本节我们来详解反射,那么关于本篇的话,我们直接来通过代码来演示反射的常用方法,以及java相应的反射实现。


关于反射的原理、反射的使用场景以及具体的示例我计划放到下篇,因为我觉得光理论是很枯燥的,而且也只有知道了怎么用,知道了自己以往是否用过,才能更好的理解其使用场景。


那么,现在我们就开始本节内容吧。


反射Reflection,是.Net Framework提供的一个帮助类库,可以读取并使用metadata,位于如下命名空间

System.Reflection

反射使用过程图解

这里先说一下我们的项目架构,有一个IDBHelper的接口类,定义了2个方法

public interface IDBHelper
{
    void Query();
    void Insert();
}

然后,分别定义了MySQL、SQLServer、Orace3个对应的类库并实现了IDBHelper接口,以MySQLHelper为例,其他类似

public class MySqlHelper : IDBHelper
{
    public MySqlHelper()
    {
        Console.WriteLine("{0}构造函数", this.GetType().Name);
    }
    public void Query()
    {
        Console.WriteLine("{0}.Query",this.GetType().Name);
    }
    public void Insert()
    {
        Console.WriteLine("{0}.Insert", this.GetType().Name);
    }
}

下面开始讲解上图步骤吧。

 

程序集加载

程序集常用加载有3种方式,分别如下所示

{
  //Load:通过程序集命名空间
  Assembly assembly = Assembly.Load("DB.MySql");
  //LoadFile:通过程序集对应的完整物理路径
  Assembly assembly1 = Assembly.LoadFile(@"E:\StydyDemos\Samples\MyReflection\MyReflection\DB.MySql\bin\Debug\DB.MySql.dll");
  //LoadFrom:通过程序集的文件名或路径加载
  //1.文件名
  Assembly assembly2 = Assembly.LoadFrom("DB.Mysql.dll");
  //2.完整物理路径
  Assembly assembly3 = Assembly.LoadFrom(@"E:\StydyDemos\Samples\MyReflection\MyReflection\DB.MySql\bin\Debug\DB.MySql.dll");
}

获取类型

通过getType()、getTypes()方法可以用来获取对应的类型

{
  Assembly assembly = Assembly.Load("DB.MySql");
  //获取程序集下全部类型,并打印其名称
  Type[] types=assembly.GetTypes();
  types.ToList().ForEach(o => Console.WriteLine(o.Name));
  //获取assembly的Type
  Type type1 = assembly.GetType();
  //获取指定类名称的类型
  Type type2 = assembly.GetType("DB.MySql.MySqlHelper");
}

结果如下,注意assembly的Type是RuntimeAssembly。

 

创建对象

类型获取到之后,我们应该来创建对象了,创建对象可通过assembly.CreateInstance()方法,如下,方法如要传入typeName参数,而且返回的是一个object对象。

因此,我们在拿到对象之后,需要对object进行类型转换,转换为我们需要的对象。

类型转换

{
  Assembly assembly = Assembly.Load("DB.MySql");
  Type type = assembly.GetType("DB.MySql.MySqlHelper");
  //创建对象
  object o = assembly.CreateInstance("DB.MySql.MySqlHelper");
  //类型转换
  MySqlHelper mySqlHelper = (MySqlHelper)o;
}

到这里的话,我们已经获取对了相应的对象了,此时的对象,和我们new出来的实例是没有区别的,此时可以通过.来调用相应的方法。那反射既然可以根据类名获取到对应的类型,那如果我们知道要调用的方法的名称,是否也能通过反射来获取到方法信息,然后通过反射的方式来调用呢?答案是可以的,下面我们来看看通过反射的方式调用方法。

方法调用

首先,我们可以通过type.GetMethods()或type.GetMethod方法来获取,分别返回MethodInfo[]和MethodInfo

{
  Assembly assembly = Assembly.Load("DB.MySql");
  Type type = assembly.GetType("DB.MySql.MySqlHelper");
  object o = assembly.CreateInstance("DB.MySql.MySqlHelper");
  //获取方法集合
  MethodInfo[] methodInfos= type.GetMethods();
  //获取特定方法名的方法
  MethodInfo methodInfo = type.GetMethod("Query");
    methodInfo.Invoke(o,null);
}

这里我们演示调用了一个public,参数为空的方法,实际上,可能有public、private、static甚至是有多个重载的方法,那么,这些调用方式分别是如何的呢?

首先,我们在这里添加几个测试类到MySqlHelper中

        #region Method
        /// <summary>
        /// 无参方法
        /// </summary>
        public void Show1()
        {
            Console.WriteLine("这里是{0}的Show1", this.GetType());
        }
        /// <summary>
        /// 有参数方法
        /// </summary>
        /// <param name="id"></param>
        public void Show2(int id)
        {
            Console.WriteLine("这里是{0}的Show2", this.GetType());
        }
        /// <summary>
        /// 重载方法之一
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        public void Show3(int id, string name)
        {
            Console.WriteLine("这里是{0}的Show3", this.GetType());
        }
        /// <summary>
        /// 重载方法之二
        /// </summary>
        /// <param name="name"></param>
        /// <param name="id"></param>
        public void Show3(string name, int id)
        {
            Console.WriteLine("这里是{0}的Show3_2", this.GetType());
        }
        /// <summary>
        /// 重载方法之三
        /// </summary>
        /// <param name="id"></param>
        public void Show3(int id)
        {
            Console.WriteLine("这里是{0}的Show3_3", this.GetType());
        }
        /// <summary>
        /// 重载方法之四
        /// </summary>
        /// <param name="name"></param>
        public void Show3(string name)
        {
            Console.WriteLine("这里是{0}的Show3_4", this.GetType());
        }
        /// <summary>
        /// 重载方法之五
        /// </summary>
        public void Show3()
        {
            Console.WriteLine("这里是{0}的Show3_1", this.GetType());
        }
        /// <summary>
        /// 私有方法
        /// </summary>
        /// <param name="name"></param>
        private void Show4(string name)
        {
            Console.WriteLine("这里是{0}的Show4", this.GetType());
        }
        /// <summary>
        /// 静态方法
        /// </summary>
        /// <param name="name"></param>
        public static void Show5(string name)
        {
            Console.WriteLine("这里是{0}的Show5", typeof(MySqlHelper));
        }
        #endregion

那这里,我们就分别看一下不同的方法如何调用吧。由于创建对象的代码都相同,以下代码之包含方法创建及调用部分。

MethodInfo methodInfo = type.GetMethod("Show1");
//指定参数类型
//MethodInfo methodInfo = type.GetMethod("Show1",new Type[] { });
methodInfo.Invoke(o,null);

带参数的方法

MethodInfo methodInfo = type.GetMethod("Show2");
//MethodInfo methodInfo = type.GetMethod("Show2",new Type[] { typeof(int) };
methodInfo.Invoke(o, new object[] { 123 });

多个重载方法的方法

如上,我们可以在获取MethodInfo的时候,传入指定的参数,由此来确认对应的重载方法,以上2个方法,之所以可以不传参数类型,是因为方法没有重载,当方法具有重载时,即使没有参数也必须传入参数,否则会抛System.Reflection.AmbiguousMatchException异常。

{//无参
    MethodInfo method = type.GetMethod("Show3", new Type[] { });
    method.Invoke(o, null);
}
{//一个参数
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(int) });
    method.Invoke(o, new object[] { 123 });
}
{//一个参数
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(string) });
    method.Invoke(o, new object[] { "画鸡蛋的不止达芬奇" });
}
{//2个参数
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(int), typeof(string) });
    method.Invoke(o, new object[] { 123, "画鸡蛋的不止达芬奇" });
}
{//2个参数
    MethodInfo method = type.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });
    method.Invoke(o, new object[] { "画鸡蛋的不止达芬奇", 123 });
}

静态方法的调用

其实,静态方法的调用比较简单,其与非静态方法的区别就是,在调用时,不是必须传入实例对象即可调用

{
    MethodInfo methodInfo = type.GetMethod("Show5");
    methodInfo.Invoke(null, new object[]{ "画鸡蛋的不止达芬奇" });
    methodInfo.Invoke(o, new object[]{ "画鸡蛋的不止达芬奇" });
}

私有方法的调用

其实,GetMethod方法是个重载方法,它除了传入方法名称,还有一个可指定搜索方式的参数,如下

那么,私有方法的获取,可以指定该参数为BindingFlags.Instance | BindingFlags.NonPublic(搜索公共及非公共成员)

{
    MethodInfo methodInfo = type.GetMethod("Show4",BindingFlags.Instance|BindingFlags.NonPublic);
    methodInfo.Invoke(o, new object[] { "画鸡蛋的不止达芬奇" });
}

 

讲到这里的话,反射的方法调用也就讲完了,那么接下来,我们再来说一下,反射类的字段和属性

反射类的属性和字段

我们这里通过创建person类来演示

    public class Person
    {
        public Person()
        {
            Console.WriteLine("{0}被创建", this.GetType().FullName);
        }
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description;
    }

和获取方法一样,获取属性和字段都属于Type上的方法

获取属性可通过GetProperties()、GetProperty()方法

获取字段可通过GetFields()、GetFiled()方法

{
    Type type1 = typeof(Person);
    object oPerson = Activator.CreateInstance(type);
    PropertyInfo info=type1.GetProperty("Name");
    foreach (var prop in type1.GetProperties())
    {
        Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(oPerson)}");
        if (prop.Name.Equals("Id"))
        {
            prop.SetValue(oPerson, 123);
        }
        else if (prop.Name.Equals("Name"))
        {
            prop.SetValue(oPerson, "画鸡蛋的不止达芬奇");
        }
        Console.WriteLine($"{type.Name}.{prop.Name}={prop.GetValue(oPerson)}");
    }
    FieldInfo fieldInfo= type1.GetField("Description");
    foreach (var field in type1.GetFields())
    {
        Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(oPerson)}");
        if (field.Name.Equals("Description"))
        {
            field.SetValue(oPerson, "画鸡蛋的不止达芬奇");
        }
        Console.WriteLine($"{type.Name}.{field.Name}={field.GetValue(oPerson)}");
    }
}

 

本节之前计划将这部分的基础操作和java中的反射技术做个对比,因为这两者很相似,这样不仅能加深印象,而且能扩宽一下知识面,因此今天在这里做个补充。java的操作操作类型如下,我就主要给出代码,如果你理解了C#,那么看懂下面的代码也不是什么难事了。

 

java反射概述

  • Class.forName()来获取Class
  • getConstructor获取构造函数
  • getMethod()获取方法
  • getField()获取字段
  • private属性,需要使用带Declared的对应方法
  • static类型的,再调用时,直接用null,无需传入实例对象即可。

反射类的构造函数

    //反射构造函数
    @Test
    public void ConstructorTest() throws Exception {
        Class aClass = Class.forName("Reflactor.Person");
        Constructor[] constructors= aClass.getConstructors();
        System.out.println("公有构造函数个数:"+constructors.length);
        /*for (Constructor constructor:constructors){
            System.out.println(constructor.getParameterCount());
        }*/
        Constructor[] constructors1= aClass.getDeclaredConstructors();
        System.out.println("全部构造函数个数:"+constructors1.length);
        //public 无参构造函数
        Constructor constructor=aClass.getConstructor();
        Person person=(Person) constructor.newInstance();
        System.out.println(person.getName());
        //public 有参构造函数,个数必须对应
        Constructor constructor1=aClass.getConstructor(String.class,int.class);
        Person person1=(Person)constructor1.newInstance("Jack",18);
        System.out.println(person1.getName());
        System.out.println(person1.getAge());
        //private 有参构造函数
        //私有构造函数,必须通过setAccessible(true)来获得访问权限
        Constructor constructor2=aClass.getDeclaredConstructor(String.class);
        constructor2.setAccessible(true);
        Person person2=(Person)constructor2.newInstance("Jack");
        System.out.println(person2.getName());
    }

 

反射类的方法

    //反射类的方法
    @Test
    public void test1() throws Exception{
        Person p=new Person();
        Class clazz=Class.forName("Reflactor.Person");
        //public
        Method method= clazz.getMethod("getAb");
        method.invoke(p);
        //private
        Method method1=clazz.getDeclaredMethod("getab");
        method1.setAccessible(true);
        method1.invoke(p);
        //private static
        Method method2=clazz.getDeclaredMethod("getAB");
        method2.setAccessible(true);
        method2.invoke(null);//static方法可传null调用
    }

 

反射类的main方法

  //获取main方法
    //注意传入参数的时候要对传入的参数进行封装。
    @Test
    public void test2() throws Exception{
        Class clazz=Class.forName("Reflactor.Person");
        /*//使用获取private的方式获取
        // wrong number of arguments
        Method method=clazz.getDeclaredMethod("main", String[].class);*/
        Method method=clazz.getDeclaredMethod("main", String[].class);
        Person person=new Person();
        //这里需要传入一个String[].class,但是invoke方法会把传入的new String[]{"1","2","3"}封装成一个参数,因此,需要在外层在封装一层。
//        method.invoke(person,new Object[]{new String[]{"1","2","3"}});
        method.invoke(null,new Object[]{new String[]{"1","2","3"}});//static方法调用时可直接传null。
    }

 

反射类的字段

//反射类的字段
@Test
public void test3() throws Exception{
    Class clazz=Class.forName("Reflactor.Person");
    System.out.println(clazz.getFields());
    Person person=new Person();
    //public 有默认值
    Field field=clazz.getField("birthday");
    String birthday=(String)field.get(person);
    System.out.println(birthday);
    //public 无默认值
    Field field2= clazz.getField("name");
    String name=(String)field2.get(person);
    System.out.println(name);
    //private 有默认值
    Field field1= clazz.getDeclaredField("weight");
    field1.setAccessible(true);
    int weight=(int)field1.get(person);
    System.out.println(weight);
    //private static
    Field field3=clazz.getDeclaredField("FirstName");
    field3.setAccessible(true);
    String firstName=(String)field3.get(null);
    System.out.println(firstName);
}

 



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
8天前
|
开发框架 Java .NET
C#与Java
在动态且不断发展的软件开发世界中,Java 和 C# 是两个巨头,每个都有自己独特的优势、理念和生态系统。本文深入比较了 Java 和 C#,探讨了它们的历史背景、语言特性、性能指标、跨平台功能等。
C#与Java
|
6天前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
10 0
[Java]反射
|
2月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
58 9
Java——反射&枚举
|
17天前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
18 2
|
2月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
15天前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
12 0
|
2月前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
2月前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)
|
2月前
|
Java
扫盲java基础-反射(二)
扫盲java基础-反射(二)
|
3月前
|
缓存 安全 Java
【Java 第十篇章】反射
Java 反射技术让程序能在运行时动态获取类信息并操作对象,极大提升了灵活性与扩展性。本文将介绍反射的基本概念、原理及应用,包括如何使用 `Class`、`Field`、`Method` 和 `Constructor` 类进行动态操作。此外,还将探讨反射在动态加载、框架开发与代码测试中的应用场景,并提醒开发者注意性能与安全方面的问题,帮助你更合理地运用这一强大工具。
28 0