首页 > 编程语言 >C# 探险之旅:第七十四节 - 反射Reflection:给代码装上“X光眼”

C# 探险之旅:第七十四节 - 反射Reflection:给代码装上“X光眼”

时间:2024-12-26 20:57:10浏览次数:7  
标签:反射 第七十四 hero Reflection C# heroType Hero Name 属性

嘿,小伙伴们!欢迎再次踏上C#探险之旅,今天我们要探索的是一个超级酷炫的特性——反射(Reflection)!想象一下,如果你的代码能像X光机一样,看透自己和其他对象的内部结构,那会是多么神奇啊!没错,反射就是给代码装上“X光眼”的秘诀!

什么是反射?

反射(Reflection)是C#中的一种强大机制,允许你在运行时检查和操作类型的信息。通过反射,你可以:

  • 查看类的属性、方法、构造函数等元数据。

  • 动态创建对象实例。

  • 动态调用方法或访问字段。

  • 修改对象的属性值。

  • 反射的特点:灵活、强大、动态。它可以让你在运行时获取和操作类型信息,而不需要在编译时就知道这些信息。

  • 反射的用途:常用于框架开发、插件系统、序列化/反序列化、依赖注入等场景。无论是在游戏开发中动态加载模块,还是在Web应用中处理复杂的配置文件,反射都能让你轻松应对。

为什么需要反射?

假设你正在开发一个游戏,游戏中有各种各样的角色,每个角色都有不同的技能和属性。但你不想为每个角色都编写单独的代码,而是希望有一个通用的系统来管理所有角色。通过使用反射,你可以在运行时动态地加载角色类,调用它们的方法,并修改它们的属性。这就像你可以用一面“魔法镜”来查看每个臣子的能力,并根据需要调整他们的任务。

反射的优势在于它的灵活性和动态性。你可以根据不同的情况,动态地调整程序的行为,而不需要编写大量的条件判断语句。这就像你可以随时调整任务的内容,而不会打乱整个王国的运作。

反射能做什么?
  1. 查看类型信息:知道一个对象是什么类型,它有哪些属性、方法。
  2. 动态调用方法:不用在编译时就知道方法的名字,运行时也能调用它。
  3. 创建对象实例:不用直接new,反射也能帮你生成对象。
  4. 访问和修改属性:就算是private的属性,反射也能摸到(但要小心哦,乱摸可能会出事)。

第一部分:简单的反射

1. 获取类型信息

首先,我们需要学习如何使用反射来获取类型的信息。这就像你可以用“魔法镜”来查看某个对象的详细信息,包括它的属性、方法和构造函数。

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 定义一个类
        public class Hero
        {
            public string Name { get; set; }
            public int Level { get; set; }

            public void Attack()
            {
                Console.WriteLine($"{Name} attacks!");
            }

            public void Defend()
            {
                Console.WriteLine($"{Name} defends!");
            }
        }

        // 创建一个Hero对象
        Hero hero = new Hero { Name = "Alice", Level = 5 };

        // 获取Hero类型的Type对象
        Type heroType = typeof(Hero);

        // 输出类名
        Console.WriteLine($"Class Name: {heroType.Name}");

        // 获取所有公共属性
        PropertyInfo[] properties = heroType.GetProperties();
        Console.WriteLine("Public Properties:");
        foreach (var property in properties)
        {
            Console.WriteLine($"- {property.Name}: {property.PropertyType}");
        }

        // 获取所有公共方法
        MethodInfo[] methods = heroType.GetMethods();
        Console.WriteLine("Public Methods:");
        foreach (var method in methods)
        {
            Console.WriteLine($"- {method.Name}");
        }
    }
}

注释解释

  • typeof(Hero):获取Hero类的Type对象,表示该类的元数据。
  • heroType.Name:获取类的名称。
  • heroType.GetProperties():获取类的所有公共属性。
  • heroType.GetMethods():获取类的所有公共方法。
  • Console.WriteLine($"- {property.Name}: {property.PropertyType}");:输出每个属性的名称和类型。
  • Console.WriteLine($"- {method.Name}");:输出每个方法的名称。
2. 动态创建对象

接下来,我们学习如何使用反射来动态创建对象实例。这就像你可以用“魔法镜”召唤出一个臣子,而不需要提前知道他的名字。

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 定义一个类
        public class Hero
        {
            public string Name { get; set; }
            public int Level { get; set; }

            public void Attack()
            {
                Console.WriteLine($"{Name} attacks!");
            }

            public void Defend()
            {
                Console.WriteLine($"{Name} defends!");
            }
        }

        // 获取Hero类型的Type对象
        Type heroType = typeof(Hero);

        // 使用反射动态创建Hero对象
        Hero hero = (Hero)Activator.CreateInstance(heroType);
        hero.Name = "Bob";
        hero.Level = 3;

        // 调用Hero对象的方法
        hero.Attack();  // 输出: Bob attacks!
        hero.Defend();  // 输出: Bob defends!

        // 或者使用反射调用方法
        MethodInfo attackMethod = heroTypeGetMethod("Attack");
        attackMethod.Invoke(hero, null);  // 输出: Bob attacks!
    }
}

注释解释

  • Activator.CreateInstance(heroType):使用反射动态创建Hero对象的实例。
  • hero.Name = "Bob";:设置对象的属性值。
  • hero.Attack();:直接调用对象的方法。
  • MethodInfo attackMethod = heroType.GetMethod("Attack");:获取Attack方法的MethodInfo对象。
  • attackMethod.Invoke(hero, null);:使用反射调用Attack方法,null表示没有参数。
3. 动态调用方法

除了创建对象,我们还可以使用反射来动态调用对象的方法。这就像你可以用“魔法镜”命令某个臣子执行特定的任务,而不需要提前知道他的能力。

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 定义一个类
        public class Hero
        {
            public string Name { get; set; }
            public int Level { get; set; }

            public void Attack(string target)
            {
                Console.WriteLine($"{Name} attacks {target}!");
            }

            public void Defend()
            {
                Console.WriteLine($"{Name} defends!");
            }
        }

        // 创建一个Hero对象
        Hero hero = new Hero { Name = "Alice", Level = 5 };

        // 获取Hero类型的Type对象
        Type heroType = typeof(Hero);

        // 获取Attack方法的MethodInfo对象
        MethodInfo attackMethod = heroType.GetMethod("Attack");

        // 动态调用Attack方法,传递参数
        object[] parameters = new object[] { "Dragon" };
        attackMethod.Invoke(hero, parameters);  // 输出: Alice attacks Dragon!

        // 获取Defend方法的MethodInfo对象
        MethodInfo defendMethod = heroType.GetMethod("Defend");

        // 动态调用Defend方法,没有参数
        defendMethod.Invoke(hero, null);  // 输出: Alice defends!
    }
}

注释解释

  • MethodInfo attackMethod = heroType.GetMethod("Attack");:获取Attack方法的MethodInfo对象。
  • object[] parameters = new object[] { "Dragon" };:准备调用Attack方法所需的参数。
  • attackMethod.Invoke(hero, parameters);:使用反射调用Attack方法,并传递参数。
  • defendMethod.Invoke(hero, null);:使用反射调用Defend方法,null表示没有参数。

第二部分:反射与属性

4. 动态访问和修改属性

反射不仅可以调用方法,还可以动态访问和修改对象的属性。这就像你可以用“魔法镜”查看和修改某个臣子的状态,而不需要提前知道他的属性。

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 定义一个类
        public class Hero
        {
            public string Name { get; set; }
            public int Level { get; set; }
        }

        // 创建一个Hero对象
        Hero hero = new Hero { Name = "Alice", Level = 5 };

        // 获取Hero类型的Type对象
        Type heroType = typeof(Hero);

        // 获取Name属性的PropertyInfo对象
        PropertyInfo nameProperty = heroType.GetProperty("Name");

        // 获取当前Name属性的值
        string currentName = (string)nameProperty.GetValue(hero);
        Console.WriteLine($"Current Name: {currentName}");  // 输出: Current Name: Alice

        // 修改Name属性的值
        nameProperty.SetValue(hero, "Bob");
        Console.WriteLine($"New Name: {hero.Name}");  // 输出: New Name: Bob

        // 获取Level属性的PropertyInfo对象
        PropertyInfo levelProperty = heroType.GetProperty("Level");

        // 获取当前Level属性的值
        int currentLevel = (int)levelProperty.GetValue(hero);
        Console.WriteLine($"Current Level: {currentLevel}");  // 输出: Current Level: 5

        // 修改Level属性的值
        levelProperty.SetValue(hero, 10);
        Console.WriteLine($"New Level: {hero.Level}");  // 输出: New Level: 10
    }
}

注释解释

  • PropertyInfo nameProperty = heroType.GetProperty("Name");:获取Name属性的PropertyInfo对象。
  • nameProperty.GetValue(hero):获取Name属性的当前值。
  • nameProperty.SetValue(hero, "Bob"):修改Name属性的值。
  • PropertyInfo levelProperty = heroType.GetProperty("Level");:获取Level属性的PropertyInfo对象。
  • levelProperty.GetValue(hero):获取Level属性的当前值。
  • levelProperty.SetValue(hero, 10):修改Level属性的值。

第三部分:反射与泛型

5. 动态创建泛型类型

反射不仅可以用于普通类,还可以用于泛型类。这就像你可以用“魔法镜”召唤出一个带有特殊能力的臣子,而不需要提前知道他的具体类型。

using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 定义一个泛型类
        public class Box<T>
        {
            public T Item { get; set; }

            public void ShowItem()
            {
                Console.WriteLine($"Box contains: {Item}");
            }
        }

        // 获取Box<T>类型的Type对象
        Type boxType = typeof(Box<>);

        // 创建Box<int>类型的Type对象
        Type intBoxType = boxType.MakeGenericType(typeof(int));

        // 使用反射动态创建Box<int>对象
        object intBox = Activator.CreateInstance(intBoxType);

        // 获取Item属性的PropertyInfo对象
        PropertyInfo itemProperty = intBoxType.GetProperty("Item");

        // 设置Item属性的值
        itemProperty.SetValue(intBox, 42);

        // 获取ShowItem方法的MethodInfo对象
        MethodInfo showItemMethod = intBoxTypeGetMethod("ShowItem");

        // 动态调用ShowItem方法
        showItemMethod.Invoke(intBox, null);  // 输出: Box contains: 42
    }
}

注释解释

  • typeof(Box<>):获取泛型类Box<T>Type对象。
  • boxType.MakeGenericType(typeof(int)):将Box<T>转换为Box<int>类型的Type对象。
  • Activator.CreateInstance(intBoxType):使用反射动态创建Box<int>对象的实例。
  • itemProperty.SetValue(intBox, 42):设置Item属性的值。
  • showItemMethod.Invoke(intBox, null):使用反射调用ShowItem方法。

挑战

我们的超级英雄类SuperHero,我们来看看反射能怎么玩它。

using System;
using System.Reflection; // 引入反射库

namespace ReflectionAdventure
{
    class SuperHero
    {
        public string Name { get; set; }
        private int Age { get; set; } // 私有属性,反射能摸到吗?

        public void Fly()
        {
            Console.WriteLine($"{Name} is flying!");
        }

        private void UsePower(string power)
        {
            Console.WriteLine($"{Name} uses {power}!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 创建一个SuperHero对象
            SuperHero hero = new SuperHero { Name = "钢铁侠", Age = 50 };

            // 获取SuperHero类型的信息
            Type heroType = typeof(SuperHero);
            Console.WriteLine($"类名:{heroType.Name}");

            // 查看所有公共属性
            Console.WriteLine("\n公共属性:");
            PropertyInfo[] properties = heroType.GetProperties();
            foreach (var prop in properties)
            {
                Console.WriteLine(prop.Name); // 输出属性名
            }

            // 反射访问并修改公共属性
            PropertyInfo nameProp = heroType.GetProperty("Name");
            Console.WriteLine($"\n原名字:{nameProp.GetValue(hero)}");
            nameProp.SetValue(hero, "蜘蛛侠"); // 修改名字
            Console.WriteLine($"新名字:{nameProp.GetValue(hero)}");

            // 反射调用公共方法
            MethodInfo flyMethod = heroType.GetMethod("Fly");
            flyMethod.Invoke(hero, null); // 调用Fly方法

            // 反射访问并调用私有方法(要小心哦!)
            MethodInfo usePowerMethod = heroType.GetMethod("UsePower", BindingFlags.NonPublic | BindingFlags.Instance);
            usePowerMethod.Invoke(hero, new object[] { "激光眼" }); // 调用私有方法

            // 反射访问私有属性(同样要小心!)
            PropertyInfo ageProp = heroType.GetProperty("Age", BindingFlags.NonPublic | BindingFlags.Instance);
            if (ageProp == null) // 因为Age是私有字段,所以GetProperty会返回null
            {
                FieldInfo ageField = heroType.GetField("Age", BindingFlags.NonPublic | BindingFlags.Instance);
                Console.WriteLine($"\n年龄(通过字段访问):{ageField.GetValue(hero)}");
            }

            Console.WriteLine("\n反射探险结束!是不是觉得代码变得透明了?");
        }
    }
}

注释解释

  1. 创建SuperHero对象:我们有一个简单的超级英雄类,有一个公共属性和一个私有属性,还有一个公共方法和一个私有方法。

  2. 获取类型信息:用typeof(SuperHero)获取SuperHero类的Type对象,它就像是类的“身份证”。

  3. 查看公共属性:用GetProperties方法获取所有公共属性,然后遍历它们,输出属性名。

  4. 反射访问并修改公共属性:用GetProperty获取Name属性,然后用GetValueSetValue方法读取和修改它的值。

  5. 反射调用公共方法:用GetMethod获取Fly方法,然后用Invoke方法调用它。

  6. 反射访问并调用私有方法:用GetMethod加上BindingFlags.NonPublic | BindingFlags.Instance获取私有方法,然后用Invoke调用它。注意,私有方法和属性是“内部秘密”,乱动可能会破坏程序的平衡哦!

  7. 反射访问私有属性:因为Age是私有字段(注意,属性通常用GetProperty,但这里是直接访问字段),所以用GetField获取它,然后用GetValue读取它的值。 


总结

通过今天的探险,我们全面学习了如何在C#中使用反射来动态地获取和操作类型信息。无论是简单的属性访问,还是复杂的泛型类型创建,反射都能让你在运行时灵活地调整程序的行为。记住,反射就像是一面“魔法镜”,它可以帮助你查看和操作对象的内部结构,确保每个臣子都能准确执行任务。

标签:反射,第七十四,hero,Reflection,C#,heroType,Hero,Name,属性
From: https://blog.csdn.net/caifox/article/details/144752809

相关文章

  • 2024最新工具分享 | 24年最新AWVS/Acunetix Premium V24.8高级版漏洞扫描器(最新版)Wi
    前言AcunetixPremium是一种Web应用程序安全解决方案,用于管理多个网站、Web应用程序和API的安全。集成功能允许您自动化DevOps和问题管理基础架构。AcunetixPremium:全面的Web应用程序安全解决方案Web应用程序对于企业和组织与客户、合作伙伴和员工的联系至关......
  • 局域网文件传输\OCR\远程控制Free Download Manager
    FreeDownloadManagerFreeDownloadManager-從網路下載任何東西OCR名称平台说明下载STranslate......
  • 导出 Swagger 接口到 Excel
       importjsonimportxlwtapi_excel=xlwt.Workbook(encoding='utf-8')#创建一个文档api_sheet=api_excel.add_sheet('CRM接口')#添加一个sheetjson_file=open('D:/Test/api-docs.txt',encoding='utf-8')#打开保存的swagge......
  • Docker之dockerfile
    DockerFileDockerfile是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。创建Dockerfile文件viDockerfile具体内容如下:FROMopenjdk:8-jdk-alpineVOLUME/tmpADDlearn-docker-storage-1.0-SNAPSHOT.jarapp.jarEXPOSE8003ENTRYPOINT["......
  • hangfire.entityframeworkcore这个库因为System.Threading.Timer未停止也未释放而导致
    如题https://github.com/sergezhigunov/Hangfire.EntityFrameworkCore/issues/32publicclassWorker:IBackgroundProcess{publicvoidExecute(BackgroundProcessContextcontext){using(varfetchedJob=connection.FetchNextJob(_queues.ToArray(),context.......
  • 【Basic Abstract Algebra】Exercises for Section 3.3 — Homomorphism of groups
    Findoutallpossiblehomomorphismfrom\(\mathbbZ_7\to\mathbbZ_{12}\).Solution:Let\(\varphi\)besuchahomomorphism.Since\(\mathbbZ_7\)isacyclicgroup,so\(\varphi\)isspecifiedby\(\varphi(\bar1)\).Since\(o(\bar1)=7......
  • SharePoint System.Net.WebException:“远程服务器返回错误: (414) REQUEST URI TOO
    SharePointCSOM执行下面代码时报错:stringfileServerRelativeUrl="/xxxx/2003249_98.RXE";using(varfileInfo=Microsoft.SharePoint.Client.File.OpenBinaryDirect(ctxOriginal,fileServerRelativeUrl))using(varreader=newStreamReader(fileInfo.Stream))......
  • SQL Server变更数据捕获(CDC)(转载)
    一、CDC简介二、开启CDC的必要条件三、开启数据库CDC1、在需要开启CDC的数据库上执行脚本如下2、查询数据库的CDC开启状态四、开启表CDC1、添加数据文件组和文件2、执行以下脚本,开启某个表的CDC3、查看某个表CDC的开启状态五、CDC使用1、对表C开启CDC后,会生成系统表以及......
  • Navicat 免费版(Navicat Premium Lite)
    NavicatPremiumLiteNavicatPremiumLite作为NavicatPremium17的精简版,可创建连接多种数据库,如MySQL、MariaDB、MongoDB、Redis、PostgreSQL、SQLite、SQLServer、OceanBase和GaussDB。它可满足广大初级用户和非商业用途的基础数据库管理需求。......
  • 2024年最新国内可用的Docker镜像加速器地址汇总
    Docker镜像加速器列表资源收集于网络,如果侵犯了您的权益,请联系我删除相关信息。 状态地址✅https://docker.1panel.live✅https://hub.rat.dev✅https://docker.actima.top✅https://atomhub.openatom.cn✅https://docker......