C# 特性

news2024/12/24 3:29:36

目录

什么是特性

Serializable

DllImport

Obsolete

Conditional

自定义Attribute


特性定义:特性其实是一个类,是直接/间接继承自Attribute,约定俗成是以Attribut结尾,在标记的时候以[   ]包裹,尾部的Attribut可以省略掉;

特性的作用:可以增加额外信息,增加额外功能,但让特性生效需要使用反射


什么是特性

Serializable

首先,当一个类需要支持序列化时,我们总能看到类名上方有如下的结构:

[Serializable]
class SampleClass
{
	// ...
}

[Serializable]就是用来标识当前类是否可序列化的。

DllImport

用来标记非.NET的函数,表明该方法在一个外部的DLL中定义。

 [DllImport("User32.dll")]
      public static extern int MessageBox(int hParent, string Message, string Caption, int Type);

 这段代码中 [DllImport]表明了MessageBox是User32.DLL中的函数,这样我们就可以像内部方法一样调用这个函数。

Obsolete

当我们的程序在迭代过程中出现了一些打算弃用的方法,如果直接删除的话,势必会影响到版本的兼容性。因此需要添加一个标识,让开发者注意到这个方法已经弃用,在未来的版本中可能会被删除。这就需要用到[Obsolete]特性:

        [Obsolete("这是需要提示的内容",true)]
        public class Test
        {

        }

当我们的程序在迭代过程中出现了一些打算弃用的方法,如果直接删除的话,势必会影响到版本的兼容性。因此需要添加一个标识,让开发者注意到这个方法已经弃用,在未来的版本中可能会被删除。这就需要用到[Obsolete]特性:

这样在调用被[Obsolete(string,bool)]标记的方法,当bool类型为true时,编译器就会进行提示并报错:

Conditional

在开发环境下,我们经常需要编写一些调试方法,而在打包时又需要去除这些代码。挨个删除显然是不现实的,此时就需要用到[Conditional]特性。它需要在参数中传递一个字符串,编译器会根据这个字符串寻找同名的宏。如果这个宏定义了,该特性修饰的方法就会启用,反之则禁用。

#define IsShowMessage

// ...
[Conditional("IsShowMessage")]
public static void Test2()
{
	Console.WriteLine("调试信息。。。。");
}
// ...

因此在调试过程中,先用[Conditional]特性标记调试方法,调试完毕将#define IsShowMessage注释即可一键禁用标记过的调试方法。

通过上面的几个示例,我们再来理解特性的定义:

  • 将应用了特性的程序结构叫做目标
  • 设计用来获取和使用元数据的程序(对象浏览器)叫做特性的消费者
  • NET预定了很多特性,我们也可以声明自定义特性

重要的一点就是Attribute就是一个类,Attribute类是在编译的时候被实例化的,而不是像通常的类那样在运行时候才实例化。

我们对编写的类、方法、属性,方法的参数,方法的返回值等通过特性进行标记,编译器就会根据特性产生元数据,再将这些元数据放入程序集中。消费者就可以获取到这些元数据,并进行使用。

自定义Attribute

首先,特性是一个类。在自定义特性时,我们自定义的特性类名中必须包含Attribute,同时继承(直接继承或者间接继承)Attribute类。然后需要在该类上通过AttributeUsage特性指明自定义特性的应用范围。

 /// <summary>
/// 自定义特性
/// AllowMultiple =true:标记在特性上的特性,其实是对特性的一种约束;
/// Inherited =true:约束当前特性是否可以继承
/// AttributeTargets.All:当前特性可以标记在所有的元素上
/// AttributeUsage:在定义特性的时候,对特性的一种约束
/// </summary>
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class CustomAttribute : Attribute
{
        public CustomAttribute()
        {

        }
        public CustomAttribute(int id)
        {

        }
        public CustomAttribute(string name)
        {

        }
        public CustomAttribute(int id,string name)
        {
            this.Id = id;
            this.Name = name;
        }
        public string Description;

        public int Id { get; set; }
        public string Name { get; set; }         
        public int Do()
        {
            return 1;
        } 
    }
}

利用反射获取Attribute信息并执行方法

public static void ReflectionArrtibute<T>(T t) where T : Class
{
      Type type = t.GetType();//通过参数来获取Type类型
      if (type.IsDefined(typeof(CustomAttribute),true)) //反射调用特性,记得一定要先判断,然后再获取
      { 
          foreach (CustomAttribute attribute in type.GetCustomAttributes()) //把所有标记的特性都实例化了
          {
              Console.WriteLine($"attribute.Id={attribute.Id}");
              Console.WriteLine($"attribute.Name={attribute.Name}");
              attribute.Do();
          }
          ///循环获取所有的属性上标记的特性,调用特性中的元素
          foreach (PropertyInfo prop in type.GetProperties())
          { 
              if (prop.IsDefined(typeof(CustomAttribute), true))
              {
                  foreach (CustomAttribute attribute in prop.GetCustomAttributes()) //把所有标记的特性都实例化了
                  {
                      Console.WriteLine($"attribute.Id={attribute.Id}");
                      Console.WriteLine($"attribute.Name={attribute.Name}");
                      attribute.Do();
                  }
              }
          }
          ///循环获取所有的字段上标记的特性,调用特性中的元素
          foreach (FieldInfo field in type.GetFields())
          {
              if (field.IsDefined(typeof(CustomAttribute), true))
              {
                  foreach (CustomAttribute attribute in field.GetCustomAttributes()) //把所有标记的特性都实例化了
                  {
                      Console.WriteLine($"attribute.Id={attribute.Id}");
                      Console.WriteLine($"attribute.Name={attribute.Name}");
                      attribute.Do();
                  }
              }
           }
          ///循环获取所有方法上标记的特性,调用特性中的元素
          foreach (MethodInfo method in type.GetMethods())
          {
              if (method.IsDefined(typeof(CustomAttribute), true))
              {
                  foreach (CustomAttribute attribute in method.GetCustomAttributes()) //把所有标记的特性都实例化了
                  {
                      Console.WriteLine($"attribute.Id={attribute.Id}");
                      Console.WriteLine($"attribute.Name={attribute.Name}");
                      attribute.Do();
                  }
              }
          } 
}

将枚举值的中文扩展信息添加到Attribute

//新增ExplainAttribute
[AttributeUsage( AttributeTargets.All,AllowMultiple =true,Inherited =true)]
public class ExplainAttribute :Attribute
{
        public string Explain { get; private set; } 
        public ExplainAttribute(string explain)
        {
            this.Explain = explain;
        } 
}
//创建枚举
public enum GameState
{
        [ExplainAttribute("开始")]
        Start,
        [ExplainAttribute("暂停")]
        Stop,
        [ExplainAttribute("继续")]
        GoOn
}
//获取Attribute信息
public static class ExplainExtension
{
        public static string GetExplain(this Enum @enum)
        {
            Type type = @enum.GetType();

            FieldInfo field = type.GetField(@enum.ToString());

            if (field != null)
            {
                if (field.IsDefined(typeof(ExplainAttribute), true))
                {
                    ExplainAttribute attribute = field.GetCustomAttribute<ExplainAttribute>();

                    return attribute.Explain;
                }
            }
            return @enum.ToString();
        }
 }

使用Attribute验证输入信息

//定义用于验证的Attribute
 [AttributeUsage(AttributeTargets.Property)]
 public class MobileNumAtrribute : AbstractValidateAttribute
 {
        public int Count { get; private set; }

        public MobileNumAtrribute(int count)
        {
            if (count<=0)
            {
                throw new Exception("手机号长度不可能为负数");
            }
            Count = count;
        }
        public override bool Validate(object mobileNum)
        { 
            return mobileNum != null && mobileNum.ToString().Length == Count; 
        } 
 }
//定义需要验证的类
 public class Student
 {
        public string Description; 
        public string QQ { get; set; }
        [MobileNumAtrribute(11)]
        public long MobileNum { get; set; } 
 }
//验证
public static bool Validate<T>(T t)
{ 
            Type type = t.GetType();
            foreach (PropertyInfo prop in type.GetProperties())
            {
                / 验证手机号长度
                if (prop.IsDefined(typeof(MobileNumAtrribute), true))
                {
                    object oValue = prop.GetValue(t);
                    MobileNumAtrribute atrribute = prop.GetCustomAttribute<MobileNumAtrribute>(true);
                    if (!atrribute.Validate(oValue))
                    {
                        return false;
                    }
            }
            return true;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/652925.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

竞赛知识点5【图论】

文章目录 前言基本概念图的定义和分类路径顶点的度、入度、出度图的存储图的DFS遍历图的BFS遍历判断是否为欧拉图拓扑排序关键路径前言 图论起源于著名的哥尼斯堡七桥问题——从这四块陆地中任何一块开始,通过每一座桥正好 一次,再回到起点。欧拉在 1736 年解决了这个问题,…

【计算机视觉】千字汇总:一文读懂计算机视觉,干货满满记得收藏

文章目录 一、前言二、计算机视觉为什么重要三、什么是计算机视觉四、计算机视觉的基本原理五、计算机视觉的典型任务5.1 图像分类5.2 目标检测5.3 语义分割5.4 实例分割5.5 目标追踪 六、计算机视觉在日常生活中的应用场景6.1 门禁、支付宝上的人脸识别6.2 停车场、收费站的车…

Android——布局管理器(十一)

1. 线性布局管理器&#xff1a;LinearLayout 1.1 知识点 &#xff08;1&#xff09;布局管理器简介&#xff1b; &#xff08;2&#xff09;线型布局管理器的配置&#xff1b; &#xff08;3&#xff09;通过Activity程序进行线型布局 1.2 具体内容 在android中&#xff…

java elasticsearch 实现以图搜图效果

前言&#xff1a; 现在需要用javaelasticsearch的方式实现以图搜图的效果&#xff0c;根据下面的文章内容做了一点修改 相关文章&#xff1a;https://blog.csdn.net/m0_52640724/article/details/129357847 一、相关环境 java&#xff1a;jdk11 elasticsearch&#xff1a;7.17…

行情不好进阶困难?那是因为你没有选对方向

关注“软件测试藏经阁”微信公众号&#xff0c;回复暗号【软件测试】&#xff0c;即可获取氪肝整理的全套测试资源 IT行情彻底崩盘了&#xff1f; 相信凡是在抖音关注过互联网相关内容的同学&#xff0c;应该会经常听到这句话吧&#xff01;没错就是号称“某蛙之父”的那个人天…

【LeetCode】HOT 100(13)

题单介绍&#xff1a; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合初识算法与数据结构的新手和想要在短时间内高效提升的人&#xff0c;熟练掌握这 100 道题&#xff0c;你就已经具备了在代码世界通行的基本能力。 目录 题单介绍&#…

JavaScript中getElementById与querySelector区别

JavaScript中getElementById与querySelector区别 1、getElement(s)Byxxxx 的用法1.1 getElementById() 方法1.2 getElementsByClassName() 方法1.3 getElementsByTagName() 方法 2、querySelector() 和 querySelectorAll() 用法2.1 querySelector() 方法2.2 querySelectorAll()…

Big O示例与Python数据结构的Big O

在Big-O示例部分的第一部分&#xff0c;我们将介绍各种Big-O函数的各种迭代。 让我们从一些简单的例子开始&#xff0c;探索大O是什么。 O(1) Constant def func_constant(values):Prints first item in a list of values.print(values[0])func_constant([1,2,3])# 1请注意&a…

避雷器计数器测试仪

原理 图1所示为JS动作记数器的原理接线图。图1&#xff08;a&#xff09;为JS动作记数器的基本结构&#xff0c;即所谓的双阀片式结构。当避雷器动作时&#xff0c;放电电流流过阀片R1&#xff0c;在R1上的压降经阀片R2给电容器C充电&#xff0c;然后C再对电磁式记数器的电感线…

easyX基本概念(注释版)

0.前言 本次我给您带来easyX库系列的博文&#xff0c;本系列博文目的在于对原easyX库文档进行一个补充和注解&#xff0c;重在补充测试样例和实践。 easyX库本身并不值得过于学习&#xff0c;但是作为有C语言基础的C爱好者&#xff0c;学习easyX能让您对IT技术更加感兴趣。用…

AI完成音频创作,击败99%作者

使用AI完成音频创作 &#xff0c;击败99%同类创作者 &#xff0c;享受持续广告变现收益 &#xff0c;下面我们来看下如何使用把~ 音频生成和投放可以分为以下两步骤&#xff1a; 使用AI效能公众号完成内容创作&#xff1b;利用喜马拉雅云剪辑发布内容 1. 内容生成 打开AI效能公…

Altium Designer 15 (AD15)新建元件库

1. 连接线 1.1 设置连接线的默认颜色&#xff0c;宽度&#xff1a;点击图标 --》按Tab键 1.2 默认使用蓝色&#xff0c;通用 2. 调出来元件库&#xff0c;然后按照元件的英文名字搜元件&#xff0c;拖到左边画图区域就可以了 3. 自己画一个元器件&#xff0c;自定义元器件 3.1…

6.14 消息队列

目录 消息队列 消息队列结构 消息队列使用步骤 消息队列创建/打开-msgget 消息队列创建/打开 - 示例 消息发送 – msgsnd 消息格式 消息发送 - 示例 笔记 消息队列 消息队列是System V IPC对象的一种 消息队列由消息队列ID来唯一标识 消息队列就是一个消息的列表。…

解决Idea中日志文件log4j.xml中http//jakarta.apache.org/log4j爆红,报错此 uri is not registered

在Idea中&#xff0c;配置log4j.xml出现“http //jakarta.apache.org/log4j/ uri is not registered”的错误信息&#xff0c;解决步骤如下&#xff1a; 1、原始的log4j.xml配置文件&#xff1a; <?xml version"1.0" encoding"GB2312" ?> <!…

ESP32(Micro Python)LVGL 四路ADC

本程序布局与上一个程序相同&#xff0c;引脚不重合&#xff0c;可以在不更换外设的情况下切换程序。由于仪表盘显示的数值范围不可调&#xff0c;实际显示的值为测量值占量程的百分比。 代码如下 import lvgl as lv import time from espidf import VSPI_HOST from ili9XX…

chatgpt赋能python:Python如何在输入之前等待30秒

Python如何在输入之前等待30秒 作为一名编程工程师&#xff0c;程序的性能和用户体验都是非常重要的。在用户输入数据之前等待一段时间可以帮助我们避免不必要的错误和提高程序的稳定性。本文将介绍如何使用Python等待30秒在输入数据。 使用Python的time模块 Python的time模…

【强烈推荐】 十多款2023年必备国内外王炸级AI工具 (免费 精品 好用) 让你秒变神一样的装逼佬感受10倍生产力 (4) AI办公

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

SLAM实战项目(2) — ROS下运行ORB-SLAM2稠密地图重建

目录 1 运行步骤 (1) 创建工作空间 (2) 修改CmakeList.txt (3) 编译 (4) 下载bag文件 (4) 编写roslaunch文件 2 运行报错 报错1&#xff1a; 报错2&#xff1a; 报错3&#xff1a; 报错4&#xff1a; ROS学习文档&#xff1a;Introduction Autolabor-ROS机器人入门…

Bert模型精讲

1.Autoregressive语言模型与Autoencoder语言模型 1.1 语言模型概念介绍 Autoregressice语言模型&#xff1a;指的是依据前面(或后面)出现的单词来预测当前时刻的单词&#xff0c;代表有ElMo, GPT等。 Autoencoder语言模型&#xff1a;通过上下文信息来预测被mask的单词&…

Linux GCC,GDB,Shell脚本,Vim的简单使用

这里写目录标题 GCC命令GDB命令Shell脚本VIM指令 GCC命令 GCC&#xff08;GNU Compiler Collection&#xff0c;GNU编译器套件&#xff09;是由GNU开发的编程语言译器 编译一个简单的.c程序&#xff1a; 四步分开写&#xff1a; gcc -E -o hello.i hello.c // 预处理 gcc -…