首页 > 编程语言 >C#反射和特性

C#反射和特性

时间:2023-06-03 21:22:12浏览次数:43  
标签:反射 Console string C# 程序 特性 WriteLine Type

参考文章:https://www.cnblogs.com/moonache/p/7532006.html

元数据和反射

什么是反射?

  • 有关程序及其类型的数据被称为元数据(metadata),它们保存在程序的程序集中
  • 程序在运行时,可以查看其他程序集或其本身的元数据。一个运行的程序査看本身的元数据或其他程序的元数据的行为叫做反射(reflection)

那么怎样来反射出数据呢?

BCL声明了一个叫做Type的抽象类,它被设计用来包含类型的特性。使用这个类的对象能让我们获取程序使用的类型的信息。

image

需要了解的有关Type的重要事项如下:

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

object.GetType()

    /// <summary>
    /// 反射操作(2种:object.GetType() & typeof):一个运行的程序査看本身的元数据或其他程序的元数据的行为叫做反射(reflection)
    /// 有关程序及其类型的数据被称为元数据(metadata),它们保存在程序的程序集中
    /// </summary>
    static void ReflectOp()
    {
        // 使用 object.GetType()
        var bs = new MyBaseClass();
        var ma = new MyClassA();

        MyBaseClass[] bsma = new MyBaseClass[] { bs, ma };

        foreach (var i in bsma)
        {
            Type t = i.GetType();

            Console.WriteLine("对象类型名称:" + t.Name);

            FieldInfo[] ins = t.GetFields();
            foreach (FieldInfo fi in ins)
            {
                Console.WriteLine("     字段名称包含:" + fi.Name);
            }
        }
    }

typeof关键字

        /// <summary>
        /// 反射操作(2种:object.GetType() & typeof):一个运行的程序査看本身的元数据或其他程序的元数据的行为叫做反射(reflection)
        /// 有关程序及其类型的数据被称为元数据(metadata),它们保存在程序的程序集中
        /// </summary>
        static void ReflectOp()
        {
            // 使用typeof关键字
            Type bs = typeof(MyClassA);
            Console.WriteLine("类型名称:" + bs.Name);

            FieldInfo[] bsfs = bs.GetFields();
            foreach (FieldInfo fi in bsfs)
            {
                Console.WriteLine("     字段名称包含:" + fi.Name);
            }
        }

特性

什么是特性?

特性(attribute)是一种允许我们向程序的程序集增加元数据的语言结构。它是用于保存程序结构信息的某种特殊类型的类。

这么看不太理解,先让我们看看特性是怎么使用的吧。

特性的使用

预定义的特性

#define DoRun
...// 其他代码

        /// <summary>
        /// 使用Obsolete废弃特性
        /// 警告他们不要使用旧方法(有新方法代替),可以使用Obsolete特性将程序结构标注为过期的,并且在代码编译时显式有用的警告消息。
        /// </summary>
        [Obsolete("方法已弃用")]
        static void PintOut()
        {
            Console.WriteLine("这是一个打印方法。");
        }

        /// <summary>
        /// 使用Conditional特性(相当于C的条件编译)
        /// Conditional特性允许我们包括或排斥特定方法的所有调用。为方法声明应用Conditional特性并把编译符作为参数来使用。
        ///     如果定义了编译符号,那么编译器会包含所有调用这个方法的代码,这和普通方法没有什么区别
        ///     如果没有定义编译符号,那么编译器会忽略代码中这个方法的所有调用
        /// </summary>
        /// <param name="message"></param>
        [Conditional("DoRun")]// 这里在第一行定义了 DoRun
        static void RunMessage(string message)
        {
            Console.WriteLine(message);
        }

        /// <summary>
        /// 调用者信息特性可以访问文件路径、代码行数、调用成员的名称等源代码信息。
        ///     - 这三个特性名称为CallerFilePath、CallerLineNumber和CallerMemberName
        ///     - 这些特性只能用于方法中的可选参数:如果调用方法时显式指定了这些参数,则会使用真正的参数值。
        ///         + 但在此方法中调用时,没有显式提供这些值,
        ///         + 因此系统将会提供源代码的文件路径、调用该方法的代码行数和调用该方法的成员名称
        /// </summary>
        /// <param name="message"></param>
        /// <param name="fileName"></param>
        /// <param name="lineNumber"></param>
        /// <param name="callingMember"></param>
        static void MyTrace(string message,
                               [CallerFilePath] string fileName = "",
                               [CallerLineNumber] int lineNumber = 0,
                               [CallerMemberName] string callingMember = "")
        {
            Console.WriteLine("File:         {0}", fileName);
            Console.WriteLine("Line:         {0}", lineNumber);
            Console.WriteLine("Called From:  {0}", callingMember);
            Console.WriteLine("Message:      {0}", message);
        }

        /// <summary>
        /// 测试DebuggerStepThrough特性方法
        /// </summary>
        static void TestDebuggerStepThroughAttribute()
        {
            var p = new MyDebuggerStepThroughAttributeTest();
            p.IncrementFields();
            p.X = 5;
            Console.WriteLine("测试MyDebuggerStepThroughAttributeTest特性:\n"
                + "     X = {0}, Y = {1}", p.X, p.Y);
        }

其他预定义特性如下:

image

自定义特性

有关特性类的一些要点如下:

  • 声明一个派生自System.Attribute的类
  • 给它起一个以后缀Attribute结尾的名字
  • 由于特性持有目标的信息,所有特性类的公共成员只能是:字段、属性、构造函数

那么我们该如何访问特性呢?

使用IsDefined方法

我们可以使用Type对象的IsDefined方法来检测某个特性是否应用到了某个类上。

[AttributeUsage(AttributeTargets.Class)]
public sealed class ReviewCommentAttribute:System.Attribute
{…}

[ReviewComment("Check it out","2.4")]
class MyClass{}

class Program
{
    static void Main()
    {
        var mc=new MyClass();
        Type t=mc.GetType();
        bool isDefined=
            t.IsDefined(typeof(ReviewCommentAttribute),false);
        if(isDefined)
            Console.WriteLine("ReviewComment is applied to type {0}",t.Name);
    }
}

image

使用GetCustomAttributes方法

GetCustomAttributes方法返回应用到结构的特性的数组。

  • 实际返冋的对象是object的数组,因此我们必须将它强制转换为相应的特性类型
  • 布尔参数指定是否搜索继承树来査找特性
  • object[] AttArr = t.GetCustomAttributes(false);
  • 调用GetCustomAttributes方法后,每一个与目标相关联的特性的实例就会被创建
using System;

[AttributeUsage(AttributeTargets.Class)]
public sealed class MyAttributeAttribute:System.Attribute
{
    public  string  Description  {get;set;}
    public  string  VersionNumber{get;set;}
    public  string  ReviewerID   {get;set;}

    public MyAttributeAttribute(string desc,string ver)
    {
        Description=desc;
        VersionNumber=ver;
    }
}

[MyAttribute("Check it out","2.4")]
class MyClass
{
}
class Program
{
    static void Main()
    {
        Type t=typeof(MyClass);
        object[] AttArr=t.GetCustomAttributes(false);

        foreach(Attribute a in AttArr)
        {
            var attr=a as MyAttributeAttribute;
            if(null!=attr)
            {
                Console.WriteLine("Description    :{0}",attr.Description);
                Console.WriteLine("Version Number :{0}",attr.VersionNumber);
                Console.WriteLine("Reviewer ID    :{0}",attr.ReviewerID);
            }
        }
    }
}

image

标签:反射,Console,string,C#,程序,特性,WriteLine,Type
From: https://www.cnblogs.com/swbna/p/17454669.html

相关文章

  • 帝国CMS刷新数据表news提示update ***_ecms__index set havehtml=1 where id='' limit
    今天我在进行“数据更新”时,点击“刷新所有信息内容页面”后,在“刷新数据表:article”一项出现提示Table‘empirecms.phome_ecms_’doesn’texist代码如下:Table'www_zwwiki_com.***_ecms_news_data_'doesn'texist;selectkeyid,dokey,newstempid,closepl,infotags,befrom......
  • Map 接收 @RequestBody,Controller 层可以打印值,但是 mybatis 编译 SQL 语句显示 NULL
    mybatis#{}获取的key是否一致;前端发送请求类型是否与后端接口定义的请求类型一致;前端发送请求与接口请求的类型一致的情况下,检查post请求封装data时是否以get形式传递数据。我的问题是第三个,尤其是后端不报任何错误的情况下,第三种情况极有可能。我把axios.post......
  • 使用vscode sftp插件快速上传源码文件
    1.首先安装vscode插件2.使用ctrl+shift+p或者view-commandpalette打开命令面板,输入sftp并按enter键,出现编辑配置文件界面3.输入对应的主机名,密码,或者密钥文件即可{"name":"47.100.101.152","host":"47.100.101.152","protocol":"sftp",......
  • 如何为布局增加滚动条_使用ScrollPane
    如何为布局增加滚动条_使用ScrollPane当我们使用某些布局时候,比如VBox,其容纳的控件超过了显示大小时,我们可能无法看到这些控件。解决方案是为Vbox增加一个滚动条,从而可以滚动地显示更多内容,这就要用到ScrollPane。ScrollPane是一种特护的控件,它可以设置一个布局作为其内容,然......
  • LeetCode 501. 二叉搜索树中的众数
    classSolution{public:vector<int>res;intcnt=0;intfind(TreeNode*root,intval)//返回当前子树值为val的个数{if(!root)return0;returnfind(root->left,val)+find(root->right,val)+(val==root->val);}map&......
  • 使用vscode remote连接上SSH服务器实现远程开发
    在VSCode中设置服务器信息1.首先在VsCode中安装Remote-SSH插件,直接搜索安装就行2.接下来打开Remote-SSH插件,并点击配置(Configure)3.在顶部弹出的框中选择config文件的存放位置(默认最上面的即可)4.点击后即可打开config文件,如下代码所示,Host部分输入服务器的外号(如“阿里云”),H......
  • 聊聊Spring Cloud Gateway
    网关概述整体来看,网关有点类似于门面,所有的外部请求都会先经过网关这一层。网关不仅只是做一个请求的转发及服务的整合,有了网关这个统一的入口之后,它还能提供以下功能。针对所有请求进行统一鉴权、限流、熔断、日志。协议转化。针对后端多种不同的协议,在网关层统一处理后以HT......
  • 日期筛选视图-MonthArchiveView
    2023.6.3日期筛选视图是根据模型里的某个日期字段进行数据筛选的,然后将符合结果的数据以一定的形式显示在网页上。简单来说,在列表视图ListView或详细视图DetailView的基础上增加日期筛选所实现的视图类●MonthArchiveView是在数据表筛选某个日期字段某年某月的所有的数据,默认以升......
  • 文心一言 VS 讯飞星火 VS chatgpt (30)-- 算法导论5.2 2题
    二、在HIRE-ASSISTANT中,假设应聘者以随机顺序出现,你正好雇用两次的概率是多少?文心一言:我们可以先计算出在HIRE-ASSISTANT中,雇佣一个应聘者的概率为3/4,然后计算出雇佣两次应聘者的概率为3/4*3/4*2。已知在HIRE-ASSISTANT中,雇佣一个应聘者的概率为:0.75根据概率乘法原理,可计算出......
  • 第十届蓝桥杯c++b组国赛题解(还在持续更新中...)
    试题A:平方序列解题思路:直接枚举一遍x的取值,然后按照题目给定的式子算出y,每次取x+y的最小值即可答案为7020代码实现:#include<iostream>#include<algorithm>#include<cmath>usingnamespacestd;#defineintlonglongconstintN=1e4+5;signedmain(){ //记录答案......