首页 > 编程语言 >[转]C#---特性与反射

[转]C#---特性与反射

时间:2023-06-13 09:44:24浏览次数:85  
标签:反射 Console string C# 特性 --- url public HelpAttribute

C#---特性与反射

 

所有 .NET 支持的语言编写出来的程序,在对应的编译器编译之后,会先产出程序集,其主要内容是中间语言 IL 和元数据
之后,JIT 再将 IL 翻译为机器码(不同机器实现方式不同)。
IL 使得跨平台成为可能,并且统一了各个框架语言编译之后的形式,使得框架实现的代价大大降低了。
比如,.NET 框架有N种语言,那么每种语言都必须有自己的编译器。而 .NET 框架又决定跨 M 种平台,那么,就需要有 M 种 JIT。
如果不存在 IL,则 .NET 框架为了支持 N 种语言跨 M 种平台,需要 MxN 个编译器。
但如果所有 .NET 框架的 N 种语言经过编译之后,都变成相同的形式,那么只需要 M+N 个编译器就可以了。因此,IL 大大降低了跨平台的代价。

.Net中的类都被编译成IL,反射就可以在运行时获得类信息(有哪些方法,字段,构造函数,父类),还可以动态创建对象,调用成员

每个类都对应一个Type对象,每个方法对应一个MethodInfo对象,每一个属性对应一个PropertyInfo对象
这些就是类,方法,属性的元数据对象。和这个类的对象没有直接关系。
这些元数据对象和类的成员有关,和类的对象无关,也就是每一个成员对应一个对象。
类信息对象叫做Type

exe与dll

exe文件与dll文件的主要区别是:exe文件有程序入口,而dll文件则是一种库(Dynamic Link Library)

元数据metadata

描述exe/dll文件的一个数据清单,JIT在编译时就需要读取metadata数据(在 JIT 的编译过程中会执行验证,通过将代码和元数据中的定义进行比对,确定代码的类型安全性。)

使用反射可以获取操作元数据metadata

===================================================================

Type

获取类信息对象Type的方法:

  • 从类的对象获取:Type type = person.getType();
  • 从类名获取:Type type = typeof(Person); //typeof是关键字
  • 从类的全名(命名空间+类名)获取:Type type = Type.GetType(“xxnamespace.Person”);

为什么
所有的对象都实现了GetType方法(所有对象都派生自object,而object实现了GetType方法)

特性

定义

野生定义:特性是一种允许我们向程序的程序集添加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的类

特性可以告诉编译器把程序结构的某组元数据嵌入程序集(如编译器指令和注释、描述、方法、类等其他信息),它可以放置在几乎所有的声明中(但特定的属性可能限制在其上有效的声明类型,是的,你可以使用一种特性去限制其他特性的使用)

下面会说反射,先剧透,反射就是要操作元数据,所以特性和反射常常配合使用。

开胃

对于C/Cpp程序员,条件编译很常见了

#define Debug
...
...
#ifdef Debug
 //program
#endif

而在C#中有更优雅的条件编译,可以在想做条件编译的地方用[Conditional(“Debug”)]修饰

#define DEBUG
    using System;
    using System.Diagnostics;
    public class Myclass
    {
        [Conditional("DEBUG")]						//此处使用 [Conditional("Debug")]
        public static void Message(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class Test
    {
        static void function1()
        {
            Myclass.Message("In Function 1.");
            function2();
        }
        static void function2()
        {
            Myclass.Message("In Function 2.");
        }
        public static void Main()
        {
            Myclass.Message("In Main function.");
            function1();
            Console.ReadKey();
        }
    }

这里就完成了条件编译,此处**#define DEBUG**,所以**[Conditional(“DEBUG”)]**修饰的方法在编译和执行时会产生下

    In Main function
    In Function 1
    In Function 2

如果 **[Conditional(“Release”)]**则不会有任何结果.

从这个例子就能大致看出特性的作用了:
C# 程序中的类型、成员和其他实体支持使用修饰符来控制其行为的某些方面。例如,方法的可访问性是由 public、protected、internal 和 private 修饰符控制。
C# 整合了这种能力,以便可以将用户定义类型的声明性信息附加到程序实体,并在运行时检索此类信息程序通过定义和使用特性来指定此类额外的声明性信息

当然此处Conditional是已经预定义的特性,类似的预定义特性还有Obsolete,简单来说他可以声明一个类或方法为已过时的,并且可以传入已过时的原因

[Obsolete("ThisClass is obsolete. Use ThisClass2 instead.")]
public class ThisClass
{
}

进一步

除了预定义的特性,我们还可以自定义特性:
所有特性类都派生自标准库提供的 Attribute 基类。以下示例声明了 HelpAttribute 特性,可将其附加到程序实体,以提供指向关联文档的链接。
(声明时特性名称最好以Attribute为后缀)

using System;
public class HelpAttribute: Attribute
{
    string url;
    string topic;
    public HelpAttribute(string url)     //引用时还是会像实例化一样调用构造函数,不过只需要调用Help
    {
        this.url = url;
    }
    public string Url => url;
    public string Topic {
        get { return topic; }
        set { topic = value; }
    }
}

特性的应用方式为,在相关声明前的方括号内指定特性的名称以及任意自变量。如果特性的名称以 Attribute 结尾,那么可以在引用特性时省略这部分名称。例如,可按如下方法使用 HelpAttribute。

[Help("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes")]//引用时还是会像实例化一样调用构造函数,不过只需要调用Help	
public class Widget
{
    [Help("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes", 
    Topic = "Display")]
    public void Display(string text) {}
}
 

此示例将 HelpAttribute 附加到 Widget 类。还向此类中的 Display 方法附加了另一个 HelpAttribute。
特性类的公共构造函数控制了将特性附加到程序实体时必须提供的信息。可以通过引用特性类的公共读写属性(如上面示例对 Topic 属性的引用),提供其他信息

可以在运行时使用反射读取和操纵特性定义的元数据。如果使用这种方法请求获取特定特性,便会调用特性类的构造函数(在程序源中提供信息),并返回生成的特性实例。如果是通过属性提供其他信息,那么在特性实例返回前,这些属性会设置为给定值

具体使用方法在下面:

反射

简单来说,反射—反射工具–操作metadata元数据的工具

通过反射获取特性信息

using System;


namespace Attribute_Reflec
{
    public class HelpAttribute: Attribute
    {
        string url;
        string topic;
        public HelpAttribute(string url)
        {
            this.url = url;
        }
        public string Url => url;
        public string Topic
        {
            get { return topic; }
            set { topic = value; }
        }
    }

    [Help("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes")]
    public class Widget
    {
        [Help("https://docs.microsoft.com/dotnet/csharp/tour-of-csharp/attributes",
        Topic = "Display")]
        public void Display(string text) { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Type widgetType = typeof(Widget);
            Console.WriteLine(widgetType);
            object[] widgetClassAttributes = widgetType.GetCustomAttributes(typeof(HelpAttribute), false);
            Console.WriteLine(widgetClassAttributes[0]);

            if (widgetClassAttributes.Length > 0)
{
                HelpAttribute attr = (HelpAttribute)widgetClassAttributes[0];
                Console.WriteLine($"Widget class help URL : {attr.Url} - Related topic : {attr.Topic}");
            }

            System.Reflection.MethodInfo displayMethod = widgetType.GetMethod(nameof(Widget.Display));
            Console.WriteLine(displayMethod);
            object[] displayMethodAttributes = displayMethod.GetCustomAttributes(typeof(HelpAttribute), false);
            Console.WriteLine(displayMethodAttributes[0]);

            if (displayMethodAttributes.Length > 0)
            {
                HelpAttribute attr = (HelpAttribute)displayMethodAttributes[0];
                Console.WriteLine($"Display method help URL : {attr.Url} - Related topic : {attr.Topic}");
            }
        }
    }
}
转自 https://blog.csdn.net/weixin_43604927/article/details/121656458  

标签:反射,Console,string,C#,特性,---,url,public,HelpAttribute
From: https://www.cnblogs.com/castlewu/p/17476629.html

相关文章

  • [ABC305C] Snuke the Cookie Picker题解
    题目大意有一个\(H\timesW\)的网格,一种有一个矩形,矩形中间有一个点被挖空,求这个点的坐标。(.表示空白,#表示矩形内的点)解析观察我们可以发现,每一矩形内的个点上下左右至少会有两个是#。如图:而每一个在矩形外的点上下左右最多只有一个#。所以我们只需要找的一个.的上......
  • [ABC305D] Sleep Log题解
    题目大意给\(N\)个时刻:当\(i\)为奇数时,\(A_i\)表示刚刚起床的时刻。当\(i\)为偶数时,\(A_i\)表示开始睡觉的时刻。有\(Q\)次询问,每次求在\([l,r]\)区间内睡了多长时间。分析首先我们要考虑处理边界情况。每一次二分查找第一个大于等于\(l\)和\(r\)的时刻......
  • 安洵杯SYCCTF2023 writeup
    一、MISC1.sudoku_easy简单的数独交互,几个小注意点,每次发送level之后sleep5秒才会返回题目将形如---------------------800103720023840650410006008300001062000052407072060090160000375205019846000030000---------------------转换成二维数组进行解数独......
  • ceshi
       ......
  • Unity3D:Pick and select GameObjects
    推荐:将NSDT场景编辑器加入你的3D工具链3D工具集:NSDT简石数字孪生PickandselectGameObjects可以在Scene视图中或从Hierarchy窗口中选择一个游戏对象。也可以一次选择多个游戏对象。Unity会在Scene视图中突出显示选择的游戏对象及其子项。默认情况下,选择轮廓颜色为橙......
  • YOKOGAWA CP451-10
    W;① ⑧ 0 ③0 ① 7 7 ⑦ 5 ⑨YOKOGAWAYNT511DYOKOGAWA PW501YOKOGAWA AIP578 YOKOGAWA AIP171从IntelAtom四核1.9GHz到IntelCeleron2GHz的不同CPU版本可实现应用程序优化的计算能力,因此可视化、图像处理、PLC、运动、机器人和CNC可以在单......
  • Git Commit 规范
    GitCommit规范Git是目前最常用的版本控制系统之一,而良好的GitCommit规范能够提高代码库的可读性、可维护性和合作效率。本文将介绍一些常见的GitCommit规范,帮助开发团队更好地管理和理解代码库的变更历史。为什么需要GitCommit规范?清晰、一致的GitCommit信息对......
  • 19.详解AQS家族的成员:CountDownLatch
    关注王有志,一个分享硬核Java技术的互金摸鱼侠欢迎你加入Java人的提桶跑路群:共同富裕的Java人今天我们来聊一聊AQS家族中的另一个重要成员CountDownLatch。关于CountDownLatch的面试题并不多,除了问“是什么”和“如何实现的“外,CountDownLatch还会和CyclicBarrier进行对比:什......
  • Oracle 分组统计,按照天、月份周和自然周、月、季度和年
     1.按天selectto_char(t.STARTDATE+15/24,'YYYY-MM-DD')as天,sum(1)as数量fromHOLIDAYtgroupbyto_char(t.STARTDATE+15/24,'YYYY-MM-DD')--ORDERby天NULLSLAST; selecttrunc(t.STARTDATE,'DD')as天,sum(1)as数量from......
  • 信息可视化—《抑郁-沉默的杀手》
    主题:抑郁与健康 形式:系列海报+包装图内容分为:1、抑郁概况 2、抑郁症状与原因 3、抑郁危害 4、抑郁与治愈 设计思路—对象分析我们从年龄段、地区、性别等多个方面对抑郁占比情况进行了分析,以图表的形式将各类数据清晰的展现出来,让人们更加直观的了解抑郁的占比......