【C#进阶】C# 特性

news/2024/5/3 15:02:45/文章来源:https://blog.csdn.net/weixin_61361738/article/details/129326181
序号系列文章
10【C#基础】C# 正则表达式
11【C#基础】C# 预处理器指令
12【C#基础】C# 文件与IO

文章目录

  • 前言
  • 1,特性的概念
    • 1.1 特性的属性
    • 1.2 特性的用途
  • 2,特性的定义
    • 2.1 特性参数
    • 2.2 特性目标
  • 3,预定义特性
    • 3.1 AttributeUsage
    • 3.2 Conditional
      • 3.2.1 条件特性类
    • 3.3 Obsolete
  • 4,自定义特性
    • 4.1 声明自定义特性
    • 4.2 构建自定义特性
    • 4.3 应用自定义特性
  • 结语

前言

👍 大家好,我是writer桑,前面一章已经学习了 C# 中文件与IO的操作,那本章就开始学习 C# 中特性的使用,希望看完大家能够有所收获,感谢支持!


1,特性的概念

特性 (Attribute) 是用于在运行时传递程序中各种元素(比如类、方法、结构、组件等)的行为信息的声明性标签。可以使用特性将元数据或声明性信息与代码相关联。一个特性的声明可以通过放置在它所作用的对象上面的方括号([])来描述。

1.1 特性的属性

在 C# 中,特性的属性可以总结以下几点:

  • 特性向程序添加元数据。元数据就是“描述数据的数据”,在程序中表现为描述类型的相关信息,如编译器指令和注释、描述、方法、类等其他信息。
  • 在 C# 程序中,所有类型和成员都会有一组指定的元数据用于描述它们,也允许自定义特性来描述其他任何信息。
  • 可以将一个或多个特性应用于整个程序集、模块或较小的程序元素(例如类和属性)。
  • 特性也可以像方法和属性一样接收自变量。
  • 可以使用反射1来查看当前程序集或其他程序集的元数据的信息。

1.2 特性的用途

在项目中特性的常见用途包括但不限于以下几点:

  1. 在 Web 服务中使用 WebMethod 特性标记方法时,表示该方法应可通过 SOAP 协议2进行调用。
  2. 用于描述与本机代码互操作时如何封送方法参数。
  3. 描述类、方法和接口的 COM 属性。
  4. 从标题、版本、说明或商标方面描述程序集。
  5. 通过实时 (JIT) 编译器3控制优化,这样代码就一直都易于调试。

.Net 框架中,提供了两种类型的特性:预定义特性和自定义特性。

2,特性的定义

在 C# 中,特性的定义是使用方括号 ([]) 将特性名称括起来,并放置在特性目标4的声明上方已指定特性。可以将特性附加到几乎任何声明中,尽管有些特性会限制有效附加到某些声明的类型。

特性的定义的语法可以总结为如下:

[attribute(positional_parameters, name_parameter = value, …)]
element

  • positional_parameters: 表示特性必须具备的信息。
  • name_paramete:表示特性可选的信息。
  • element: 在这里表示特性目标4

代码示例:

using System; [Serializable]
public class SampleClass
{// 该类型的对象可以被序列化。
}

在此示例中,SerializableAttribute 特性用于将具体特征作用于 SampleCLass 类。

2.1 特性参数

特性参数包括位置参数、未命名参数和已命名参数。其中,位置参数必须以特定的顺序来指定,且不能省略。而命名参数是可选参数,通过名称来指定,可以通过任何顺序来指定。

代码示例:

[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
  • 上面的实例中的三个特性都是等同的。
  • 第一个参数是位置参数(DLL 名称),始终作为第一个参数出现。
  • 位置参数与特性构造函数的参数相对应。而已命名或可选参数与特性的字段或属性相对应。

点击了解更多特性参数的使用。

2.2 特性目标

特性目标指的是应用特性的实体。例如,特性目标可以是类、特定方法或整个程序集。一般情况,特性应用于紧跟在它后面的元素。不过,C# 特性支持显示标识,例如可以显示标识为将特性应用于方法,或者是应用于其参数或返回值。

显示标识特性目标的语法如下:

[target : attribute-list]

  • target:表示指定的特性目标值。
  • attribute-list:表示要应用的特性列表。

下表展示常用的 target 值:

目标值适用对象
assembly整个程序集
module当前程序集模块
field类或结构中的字段
event事件
method方法或 get 和 set 属性访问器
param方法参数或 set 属性访问器参数
propertyProperty(属性)
return方法、属性索引器或 get 属性访问器的返回值
type结构、类、接口、枚举或委托

代码示例:

以下示例演示如何将特性应用于方法、方法参数和方法返回值。

// 默认: 应用于方法
[ValidatedContract]
int Method1()
{ return 0; 
}// 显示指定应用于方法
[method: ValidatedContract]
int Method2()
{ return 0; 
}// 应用于参数
int Method3([ValidatedContract] string contract) 
{ return 0; 
}// 应用于返回值
[return: ValidatedContract]
int Method4()
{ return 0; 
}

以下示例演示如何将特性应用于程序集和模式。

using System;
using System.Reflection;[assembly: AssemblyTitleAttribute("Production assembly 4")]
[module: CLSCompliant(true)]

点击了解更多特性目标的使用。

3,预定义特性

所谓的预定义特性就是在 .NET 框架中预先定义好的特性,使得程序代码在编译之前就能被读取。在 .NET 框架中,提供了三种预定义特性,分别为 AttributeUsage 、Conditional 、Obsolete

3.1 AttributeUsage

特性 AttributeUsage 是属性使用的意思,用于描述特性类的使用方式。其中包括如何使用一个自定义特性类,以及特性可应用到的项目的类型。注意,使用特性修饰的类 AttributeUsage 必须 System.Attribute 直接或间接派生类,否则将发生编译时错误。

AttributeUsage 特性的语法如下:

[AttributeUsage(validon, AllowMultiple=allowmultiple, Inherited=inherited)]

  • 参数 validon 表示可被应用的特性目标值。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。上述的特性目标中的表格也展示了常用特性目标值。
  • 参数 AllowMultiple 是一个可选的,allowmultiple 选项为其提供一个布尔值。如果为 true,则该特性是多用的,如果为 false,则该特性是单用的。默认为 false 表示单用的。
  • 参数 Inherited 也是可选的,inherited 选项为其提供一个布尔值。如果为 true,表示则该类可被派生类继承。如果为 false,表示不能被继承。默认为 false 表示不可继承。

代码示例:

using System;// AttributeUsage 特性应用于 ExampleAttribute 类。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ExampleAttribute : Attribute
{// 构造函数 public ExampleAttribute(String Description_in){this.description = Description_in;}// description 字段public String description;// Description 属性public String Description{get{return this.description;}}
}

因为上例中的 AttributeUsage 特性的 validon 参数被设置为 AttributeTargets.Class ,所以只能将特性应用于类对象。而不能应用其他对象,例如方法等。示例如下:

[Example("Can be applied to classes")]
public class MyClass
{[Example("Cannot be applied to fields")]    // 错误 public String MyField;
}

代码效果:
在这里插入图片描述
上例中的 AllowMultiple 参数为 false ,表示该特性不能被重复应用多次。示例如下:

[Example("Cannot be applied more than once")]
[Example("Cannot be applied more than once")]   // 错误
public class MyClass
{// 类成员....
}

代码效果:
在这里插入图片描述
上例中的 Inherited 参数为 false,表示该特性类不能被继承。示例如下:

[Example("BaseClass")]
public class Base
{// 类成员....
}public class Derive : Base
{// 子类成员....
}public class Program
{static void Main(string[] args){// 得到指定类型的Type对象Type BaseType = typeof(Base);Type DeriveType = typeof(Derive);Console.WriteLine(BaseType);     // BaseConsole.WriteLine(DeriveType);   // Derive// 判断当前的方法是否应用了Example特性bool BaseDef = Attribute.IsDefined(BaseType, typeof(ExampleAttribute));bool DriveDef = Attribute.IsDefined(DeriveType, typeof(ExampleAttribute));Console.WriteLine(BaseDef);         // TrueConsole.WriteLine(DriveDef);        // False}
}

运行效果:
在这里插入图片描述
上例中 ,使用 Attribute.IsDefined 方法检测到了 Base 类中应用了 ExampleAttribute 特性,但是 Derive 派生类却没有应用到。因为 Inherited 参数被设置为 false ,所以 Derive 类不能继承到 ExampleAttribute 特性。

3.2 Conditional

特性 Conditional 是有条件的意思。使用特性修饰的方法也就是条件方法,条件方法的执行依赖于指定的预处理标识符。预处理标识符影响着方法调用的条件编译,这种影响取决于指定的值。例如事先用预处理器指令定义了一个 Test 字符,当条件方法指定的值也为 Test 时, 则该方法会被执行。

Conditional 特性的语法如下:

[Conditional(conditionalSymbol)]

  • 参数 conditionalSymbol 表示指定的预处理标识符。

代码示例:

#define Test
using System;
using System.Diagnostics;public class Example
{[Conditional("Test")]static void Method1(){Console.WriteLine("Method1");}static void Method2(){Console.WriteLine("Method2");}static void Main(){Method1();Method2();}
}

运行效果:
在这里插入图片描述

上例中在程序编译之前使用预处理器指令定义了 Test 字符。在使用 Conditional 修饰符修饰了条件方法 Method1 中又定义了 Test 的指定值,所以 Method 方法可以顺利运行。

代码示例:

//#define Test

运行效果:
在这里插入图片描述
反之,当 Test 指定值没有定义时(被注释),Method 方法就不能运行。

条件方法的使用收到以下几点限制:

  • 条件方法必须应用在类类型或者结构体类型上。比方说如果在接口类型上制定了 Conditional 特性,则会发生编译错误。
  • 条件方法不能使用 override 修饰符,不过,条件方法允许使用 virtual 修饰符。此类方法(指的是 virtual 关键字修饰的虚方法)的替代是隐式条件的,不能用特性显式标记 Conditional 。
  • 条件方法的返回类型必须为 void,也就是不能有返回对象。

3.2.1 条件特性类

条件特性类是使用一个或多个 Conditional 特性修饰的特性类。因此,条件特性类与在其特性中声明的条件编译符号需要关联 Conditional 。

示例如下:

文件 test.cs:

using System;
using System.Diagnostics;[Conditional("DEBUG")]
public class TestAttribute : Attribute { }

文件 test1.cs:

#define DEBUG[Test]		// 指定TestAttribute
class Class1 {}

文件 test2.cs:

#undef DEBUG[Test] 		// 未指定TestAttribute
class Class2 {}

上例中的类 Class1 和 Class2 都是用特性类修饰的 Test ,前者定义了条件 DEBUG,而后者是基于否定条件 DEBUG 。由于此符号在当前上下文定义的,而不是在基类的上下文定义的。所以类 Class1 和 Class2 属性会被 Test Class1 和 Test Class2 忽略。

3.3 Obsolete

特性 Obsolete 是过时的意思,用于标记不应再使用的类型和类型的成员。使用这个特性相当于告诉编译器丢弃某个特定的目标元素,当使用某个用 Obsolete 特性应用的对象时编译器会报出错误。在实际项目中,当一个新方法用在一个类中,但开发者还想保留类中的旧方法时,就可以把他标记为 obsolete(过时的)。

Obsolete 特性的语法如下:

[Obsolete(message,iserror)]

  • 参数 message 是字符串类型,描述应用的项目为什么过时以及该替代使用什么。
  • 参数 iserror 是布尔值类型,当布尔值为 true 时,编译器会把项目的使用当成一个错误,反之为 false 时,编译器会生成一个警告。iserror 参数是可选的,默认值为 false 。

代码示例:

using System;public class Example
{[Obsolete("不要使用OldMethod,而是使用NewMethod", true)]static void OldMethod(){Console.WriteLine("这是旧方法");}static void NewMethod(){Console.WriteLine("这是新方法");}public static void Main(){OldMethod();}
}

代码效果:
在这里插入图片描述
当把参数 iserror 换成 false 时,编译器就会生成一个警告。

代码示例:

[Obsolete("不要使用OldMethod,而是使用NewMethod", false)]

代码效果:
在这里插入图片描述

4,自定义特性

在 C# 中,允许我们定义特性类来创建自己的自定义特性,特性类直接或间接派生自 Attribute 的类。 自定义特性可用于存储声明性的信息,且可在运行时被检索。自定义特性可根据项目的实际需求进行变动,可与任何目标元素相关。

创建并使用自定义特性可总结为以下四个步骤:

  1. 声明自定义特性。
  2. 构建自定义特性。
  3. 在需要应用特性的对象上放置自定义特性。
  4. 通过反射1访问特性。(暂时不讨论)

4.1 声明自定义特性

声明一个 Author 的特性类,此类是用来存储作者的姓名和年龄信息的类型。且特性的目标值设置为 Class 和 Struct, 表示可被应用的对象仅为类和结构体。

代码示例:

using System;[System.AttributeUsage(System.AttributeTargets.Class |System.AttributeTargets.Struct)
]
public class AuthorAttribute : System.Attribute

在上面的示例中,由于该类派生自 System.Attribute ,因此它是一个自定义特性类。

4.2 构建自定义特性

构建自定义特性 AuthorAttribute,该特性存储了作者的姓名(name)和年龄(age)。

代码示例:

using System;[System.AttributeUsage(System.AttributeTargets.Class |System.AttributeTargets.Struct)
]
public class AuthorAttribute : System.Attribute
{private string name;public double age;// 构造函数public AuthorAttribute(string name){this.name = name;age = 11;}
}

在上例的构建自定义特性中,声明了两个字段,分别为 name 和 age 。再使用构造函数对其赋值。需要注意的是,name 是位置参数。 所有公共读写字段或属性都是命名参数。 在本例中,version 是唯一的命名参数。

4.3 应用自定义特性

可通过将一个或多个 Author 特性放置在类或结构体上方进行应用。

代码示例:

[Author("writer桑", age = 21)]
public class Example
{// Example类成员
}

结语

👌 以上就是 C# 特性的介绍啦,希望能够对大家有所帮助。望大家多多支持,你们的支持就是笔者创作最大的动力!


  1. 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。 ↩︎ ↩︎

  2. SOAP 是基于 XML 的简易协议,可使应用程序在 HTTP 之上进行信息交换。或者更简单地说:SOAP 是用于访问网络服务的协议。 ↩︎

  3. JIT是一种提高程序运行效率的方法。 ↩︎

  4. 特性目标是指应用特性的实体。 ↩︎ ↩︎

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年再不会Redis,就要被淘汰了

目录专栏导读一、同样是缓存,用map不行吗?二、Redis为什么是单线程的?三、Redis真的是单线程的吗?四、Redis优缺点1、优点2、缺点五、Redis常见业务场景六、Redis常见数据类型1、String2、List3、Hash4、Set5、Zset6、BitMap7、Bi…

2023款欧拉好猫上市,12.98万起

上周,2023款欧拉好猫焕新上市。2023款好猫共推出5个车型: •401km标续航,舒享型/豪华型/尊贵型,分别是12.98/13.98/14.98万元; •501km长续航,豪华型/尊贵型,分别是15.58/16.58万元;…

QT的下载与安装

下载安装工具 https://download.qt.io/official_releases/online_installers/ 双击打开安装包 一步一步安装 选择需要的包,没想好的话QT装好了也可以重新使用安装程序添加 然后就装好了

SAP UI5 Upload/Download file through NetWeaver Gateway

1、创建 SEGW对象 2、创建Entity Type 要把Media 标识打上 3、 激活对象然后到DPC Class的扩展对象里面重定义 /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_STREAM /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CREATE_STREAM /IWBEP/IF_MGW_APPL_SRV_RUNTIME~UPDATE_STREAM METHOD /iwbep/if_m…

1497. 树的遍历

文章目录1.二叉树的遍历2.二叉树的构造3.例题二叉树的构造:没有中序遍历则无法唯一构造1.二叉树的遍历 2.二叉树的构造 3.例题 一个二叉树,树中每个节点的权值互不相同。 现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。 输入格式…

5.深入理解HttpSecurity的设计

深入理解HttpSecurity的设计 一、HttpSecurity的应用 在前章节的介绍中我们讲解了基于配置文件的使用方式,也就是如下的使用。 也就是在配置文件中通过 security:http 等标签来定义了认证需要的相关信息,但是在SpringBoot项目中,我们慢慢脱离…

ubuntu20修改网卡静态ip或者动态ip

1、查看所有网卡信息 ifconfig -a 2、修改信息 sudo gedit /etc/netplan/01-network-manager-all.yaml # Let NetworkManager manage all devices on this system network:ethernets:ens33: #配置的网卡的名称dhcp4: trueens38:dhcp4: trueversion: 2renderer: networkd…

用Python按时间分割txt文件中的数据

案例 有一个监测系统,每隔两分钟就会记录一下监测结果,如下图所示:现在要求按小时将数据提取,并存为新的txt文件,也就是1天会对应有24个txt文件。先整理一下思路: 读取数据将每行数据的时间戳转换成“日期-小时”格式,并按此分类数据,存入字典 按“日期-小时”分断,将…

没有钱怎么创业?一分钱没有如何能创业成功?

限制人创业成功的从来都不是资金,而是能力,这个道理很多人都可能不懂,多数人习惯了庸庸碌碌、日复一日地打工行为,却不知如何创业,那么,没有钱怎么创业?一分钱没有如何能创业成功呢?…

【虹科案例】虹科任意波形发生器在量子计算中的应用

虹科AWG在量子计算中的应用精度在研究中始终很重要,很少有研究领域需要比量子研究更高的精度。奥地利因斯布鲁克大学的量子光学和量子信息研究所需要一个任意波形发生器(AWG)来为他们的研究生成各种各样的信号。01无线电频率第一个应用是在射…

python线上商城网站项目前台和后台源码

wx供重浩:创享日记 对话框发送:python51 获取完整源码源文件说明文档配置教程等 1、网站前台 在虚拟环境中启动程序后,使用浏览器访问“http://127.0.0.1:5000”即可进入网站前台首页。如图1所示。 单击首页左上角“注册”按钮,进…

【MySQL】第17章_触发器

第17章_触发器 在实际开发中,我们经常会遇到这样的情况:有 2 个或者多个相互关联的表,如商品信息和库存信息分别存放在 2 个不同的数据表中,我们在添加一条新商品记录的时候,为了保证数据的完整性,必须同时…

正版Scrivener 3 论文/小说写作工具神器软件

一款非常优秀的写作软件,提供了各种写作辅助功能,如标注多个文档、概述介绍、全屏幕编辑、快照等,能够轻松、便捷的辅助作者从作品构思、搜集资料、组织结构、增删修改到排版输出的整个写作流程。 作为一个专业的写作软件,Scriven…

给文档添加签名,介绍用iPhone的实例

环境:iOS 16 实现电子文档上的签名不是什么新鲜事,也不需要高级的技术,原理基本一致,就是菜单路径有所不同,故在此记录一下,不然容易忘记。 这里介绍的解决方法: 需要一个签名,背…

面向对象设计模式:行为型模式之迭代器模式

一、迭代器模式,Iterator Pattern aka:Cursor Pattern 1.1 Intent 意图 Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. 提供一种按顺序访问聚合对象的元素而不公开其基…

XShell连接ubuntu20.04.LTS

1 下载XshellXShell官方下载地址打开XSHELL官方下载地址,我们可以选择【家庭和学校用户的免费许可证】,输入邮箱之后即可获得下载链接安装非常简单,跟着提示进行即可。2 连接ubuntu2.1 查看ubuntu的ip地址输入命令查看ip地址ifconfig刚开始可…

C++ | 你真的了解namespace吗?

文章目录一、前言二、命名冲突三、命名空间1、域作用限定符2、命名空间的概念👉示例1👉示例23、命名空间的定义4、命名空间的使用① 指定命名空间访问【做项目】② 使用using部分展开【做项目】③ 使用using namespace全局展开【日常练习】5、小结解答&a…

通用业务平台设计(五):预警平台建设

前言 在上家公司,随着业务的不断拓展(从支持单个国家单个主体演变成支持多个国家多个主体),对预警的诉求越来越紧迫;如何保障业务的稳定性那?预警可以帮我们提前甄别风险,从而让我们可以在风险来临前将其消灭&#xff…

3DEXPERIENCE Works 成为了中科赛凌实现科技克隆环境的催化剂

您的企业是否想过实现设计数据的统筹管理,在设计上实现标准化,并把每位设计工程师串联起来协同办公?中科赛凌通过使用3DEXPERIENCE Works 实现了上述内容,一起来看本期案例分享吧!中科赛凌 通过其自主研发的单压缩机制冷技术实现零下190℃制…

【Linux】Linux项目自动化构建工具make makefile

文章目录1. 背景2.实例3.原理4.项目清理5. 文件属性中的三个时间6. Linux下第一个小程序——进度条6.1 前置知识1:缓冲区6.2前置知识2:回车换行6.3进度条的实现7 Linux下git的”三板斧“1. 背景 一个工程中的源文件不计其数,其按类型、功能、…