C#基础知识之特性
一、什么是特性
个人理解:特性本质上也是有一种类,通过添加特性,就可以实例化这个特性类;添加特性就是在类、方法、结构、枚举、组件等上面加一个标签,使这些类、方法、结构、枚举、组件等具有某些统一的特征,用以满足相关的需求。比如:方法的异常捕捉,你是否还在某些可能出现异常的地方(例如数据库的操作、文件的操作等)经常使用try...catch。这个时候如果使用特性,就可以大大减少方法里面的try...catch的使用。你只需要定义一个专门捕捉异常的特性类ExceptionExAttribute,然后给这个特性类做些特殊处理,比如给它增加一个AOP拦截的功能。那么在可能出现异常的方法名称上面加上一个[ExceptionEx]特性标签,这个方法就具有自动捕捉异常的能力。
官方定义:特性是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
二、规定特性(Attribute)
规定特性(Attribute)的语法如下:
[attribute(positional_parameters, name_parameter = value, ...)] element
特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
1、预定义特性(Attribute)
.Net 框架提供了三种预定义特性:
- AttributeUsage
- Conditional
- Obsolete
(1)AttributeUsage
预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。规定该特性的语法如下:
[AttributeUsage( validon,//参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。 AllowMultiple=allowmultiple,//参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。 Inherited=inherited//参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。 )]
举个例子:
using System;
using System.Diagnostics;
namespace RabbitMQTest
{
class Program
{
static void Main(string[] args)
{
//通过反射查看TestPersonAttribute
Type type = typeof(TestPersonAttribute);
var person = type.GetCustomAttributes(typeof(PersonAttribute), false);
foreach (PersonAttribute each in person)
{
Console.WriteLine("Name:{0}", each.Name);
Console.WriteLine("Age:{0}", each.Age);
}
Console.ReadKey();
}
}
/// <summary>
/// 使用AttributeUsage自定义特性PersonAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class PersonAttribute : Attribute
{
public PersonAttribute(string name, int age)
{
this.Name = name;
this.Age = age;
}
public string Name { get; set; }
public int Age { get; set; }
}
/// <summary>
/// 使用自定义特性
/// </summary>
[Person()]
[Person()]
public class TestPersonAttribute
{
}
}
(2) Conditional
Conditional这个预定义特性要好好介绍一下,因为项目中还是会用到的。#if/#endif是经常用来让同一份代码生成不同的编译结果,最常见的就是debug版和release版。但是这在实际应用中并不是非常友好,因为它们容易被滥用,其代码也难以理解或调试。举个例子:
static void Function1()
{
#if DEBUG
Console.WriteLine("In Function 1.");
#endif
}
最终会在release环境下编译后留下一个Function1()空方法,虽然在release版本中Function1()什么也不做,但是Function1()方法的加载、JIT编译和调用仍然有些开销。C#为此添加了一个Conditional特性,该特性可以标识出某种环境设置下某个方法是否应该被调用。使用这种方式来描述条件编译要比#if/#endif更加清晰。这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。规定该特性的语法如下:
[Conditional( conditionalSymbol )]
使用Conditional特性,无论是否定义了DEBUG环境变量,Function1()方法也会被编译至程序集中。这种做法看起来也似乎不那么高效,但是其中占用的仅仅是一点点磁盘空间而已。如果没有被调用,Function1()并不会加载到内存中,也不会被JIT编译。在应用多个Conditional特性时,它们之间的组合关系将为"或"(OR)。
[Conditional("DEBUG"),Conditional("Trace")]
static void Function1()
{
Console.WriteLine("In Function 1.");
}
而若想创建一个使用"与"(AND)关系的构造,就使用#if/#endif吧。。。
static void Function1()
{
#if DEBUG && RELEASE
Console.WriteLine("In Function 1.");
#endif
}
(3) Obsolete
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。规定该特性的语法如下
[Obsolete( message )] [Obsolete( message,//参数 message,是一个字符串,描述项目为什么过时的原因以及该替代使用什么 iserror//参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告) )]
下面的实例演示了该特性:
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}
当您尝试编译该程序时,编译器会给出一个错误消息说明:
Don't use OldMethod, use NewMethod instead 2、自定义特性
.Net 框架允许创建自定义特性,用于存储声明性的信息,可在运行时被检索。创建并使用自定义特性包含四个步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
(1)、声明自定义特性
一个新的自定义特性应派生自 System.Attribute 类。例如:
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class PersonAttribute : Attribute
{}
(2)构建自定义特性
让我们构建一个名为 PersonAttribute 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:
- bug 的代码编号
- 辨认该 bug 的开发人员名字
- 最后一次审查该代码的日期
- 一个存储了开发人员标记的字符串消息
我们的 PersonAttribute 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。
每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 PersonAttribute 类:
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class PersonAttribute : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public PersonAttribute (int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
(3)应用自定义特性
通过把特性放置在紧接着它的目标之前,来应用该特性:
[PersonAttribute (, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[PersonAttribute (, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[PersonAttribute (, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[PersonAttribute (, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
C#基础知识之特性的更多相关文章
- C# 篇基础知识9——特性、程序集和反射
特性(Attribute)是用于为程序元素添加额外信息的一种机制.比如记录文件修改时间或代码作者.提示某方法已经过期.描述如何序列化数据等等.方法.变量.属性.类.接口.结构体以及程序集等都是程序元素 ...
- Redis学习笔记之入门基础知识——其他特性
1.订阅(subscribe)与发布(publish) 用户订阅某一个频道,频道发布新的信息时,会将信息告知用户 2.数据安全 1) 快照持久化(时间点转储,实质是数据副本) 操作:SAVA. ...
- C#基础知识之面向对象以及面向对象的三大特性
在C#基础知识之类和结构体中我详细记录了类.类成员.重载.重写.继承等知识总结.这里就记录一下对面向对象和面向对象三大特性的广义理解. 一.理解面向对象 类是面向对象编程的基本单元,面向对象思想其实就 ...
- RabbitMQ基础知识
RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...
- [SQL] SQL 基础知识梳理(四) - 数据更新
SQL 基础知识梳理(四) - 数据更新 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5929786.html 序 这是<SQL 基础知识梳理( ...
- 前端开发:css基础知识之盒模型以及浮动布局。
前端开发:css基础知识之盒模型以及浮动布局 前言 楼主的蛮多朋友最近都在学习html5,他们都会问到同一个问题 浮动是什么东西? 为什么这个浮动没有效果? 这个问题楼主已经回答了n遍.今天则是把 ...
- TCP/IP协议(二)tcp/ip基础知识
今天凌晨时候看书,突然想到一个问题:怎样做到持续学习?然后得出这样一个结论:放弃不必要的社交,控制欲望,克服懒惰... 然后又有了新的问题:学习效率时高时低,状态不好怎么解决?这也是我最近在思考的问题 ...
- TCP/IP协议(一)网络基础知识
参考书籍为<图解tcp/ip>-第五版.这篇随笔,主要内容还是TCP/IP所必备的基础知识,包括计算机与网络发展的历史及标准化过程(简述).OSI参考模型.网络概念的本质.网络构建的设备等 ...
- Linux基础知识整理
一.基础知识 1.Linux简介 Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户.多任务.支持多线程和多CPU的操作系统.它能运行主要的UNIX工具软件 ...
随机推荐
- Springboot 拦截器的背后
今天写了个拦截器对一些mapping做了些处理,写完之后突然很想看看拦截器是怎么加进spring里面.对着源码debug了一遍.又有了新的收获. 1.拦截器的实现 1.实现HandlerInterce ...
- Spring Cloud Eureka基本概述
记一次Eureka的进一步学习. 一.Eureka简介 百科描述:Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡 ...
- SpringBoot使用@Cacheable实现最简单的Redis缓存
前言 之前我们使用过RedisTemplate来实现redis缓存,然后使用工具类来实现操作redis的存储.这样的方式好处是很自由,但是还不是最简单的处理方式.对于一些简单的应用来说,其实redis ...
- Java开发知识之Java字符串类
Java开发知识之Java字符串类 一丶简介 任何语言中.字符串都是很重要的.都涉及到字符串的处理. 例如C++中. 字符串使用内存. 并提供相应的函数进行处理 strcmp strcat strcp ...
- Docker搭建MongoDB
1. Docker搭建Mongodb 1.1 获取docker镜像 docker pull mongo 1.2 创建mongodb容器 docker run --name my-mongo -p 27 ...
- C# 【一】进程 , 线程 , 微线程 , 同步 , 异步 , 并发 , 并行 , 阻塞 , 非阻塞
一 理解篇 前言 本文仅仅用作借鉴使用,作者刚入行不久,所以请不小心看到这篇文章的朋友,手下留情. 本文以小故事的形式进行叙述,逻辑不通之处.请理解. 如有错误 ,欢迎指出. 谢谢. ...
- winform中获取指定文件夹下的所有图片
方法一: C#的IO自带了一个方法DirectoryInfo dir = new DirectoryInfo("文件夹名称");dir.getFiles();//这个方法返回值就是 ...
- Java开发笔记(三十七)利用正则串分割字符串
前面介绍了处理字符串的常用方法,还有一种分割字符串的场景也很常见,也就是按照某个规则将字符串切割为若干子串.分割规则通常是指定某个分隔符,根据字符串内部的分隔符将字符串进行分割,例如逗号.空格等等都可 ...
- 解决Azure 消息队列ServiceBus提示证书不信任无权限的问题
笔者在C# 消息队列-Microsoft Azure service bus 服务总线中使用Azure消息队列,最近偶尔会遇到请求数据不入队列的问题,查找日志,问题如下: 异常:X. certific ...
- 通过 docker 来搭建 Jenkins
mkdir /data/jenkins -p mkdir /data/jenkins/{conf,data} -p echo "Asia/Shanghai" > /data/ ...