-
2022-01-03 16:01:34
1.概述
特性本质上是用来给代码添加额外信息的一种手段,它可以应用于类、结构、方法、构造函数等。在 C# 中,特性是继承自
Attribute
基类的类。所有继承自Attribute
的类都可以用作给代码添加额外信息。2.使用特性
例如,有一个名为
ObsoleteAttribute
的特性。 它用于示意代码已过时,不再使用。 可以使用方括号将此特性应用于某个类或者其他。[Obsolete] public class TestClass { }
虽然此特性类的名称为
ObsoleteAttribute
,但只需在代码中使用Obsolete
。 这是 C# 得一项约定。虽然也可以使用全名ObsoleteAttribute
。将类标记为已过时时还可以通过将参数传递给 Obsolete 特性额外添加一些说明,只能传递
bool, int, double, string, Type, enums, etc
和这些类型的数组。 不能使用表达式或变量。[Obsolete("TestClass已经过时,请使用NewTestClass")] public class TestClass { }
3.自定义特性
自定义特性只需要让一个类继承Attribute类并且名称后缀是Attribute结尾即可。
class MsgAttribute : Attribute { private string msg; public MyAttribute(string msg) { this.msg = msg; } public void ShowMsg() { Console.WriteLine(msg); } } [Msg("这是一条信息")] class MyClass { }
4. 读取特性信息
特性只做标识所用。不借助一些外在力量,特性其实什么用也没有。
若要使用特性,通常需要使用反射。例如,可以使用反射获取类的相关信息。
TypeInfo typeInfo = typeof(MyClass).GetTypeInfo();//获取类型信息 MsgAttribute customAttribute = typeInfo.GetCustomAttribute<MsgAttribute>(); //获取特性信息,返回特性的实例对象 customAttribute.GetName();//调用方法 控制台中输出 这是一条信息
GetCustomAttribute<T>泛型方法用于获取某个特定的特性,返回该特性实例,还有一个GetCustomAttributes()方法用于获取所有的特性。
特性只有使用
GetCustomAttribute
或GetCustomAttributes
,它们才会实例化。 并且每次调用都会重新实例化并返回不同的特性实例。5.总结
特性可以提供声明能力,但它们是一种元数据形式的代码,本身并不执行操作。
更多相关内容 -
C#特性源码案例2 c#经典案例.txt
2022-05-07 22:00:55C#特性源码案例2 c#经典案例.txt -
c#特性(Attribute)简单示例
2021-12-13 16:12:08c#特性(Attribute)简单示例,适合初学者 -
C# 特性
2019-01-24 11:29:20特性 引 先看一个例子: 在VS中添加一个类库项目和一个控制台项目,在控制台项目中编写以下代码。(控制台项目添加类库项目的程序集) static void Main(string[] args) { Assembly assembly = Assembly....特性
引
先看一个例子:
在VS中添加一个类库项目和一个控制台项目,在控制台项目中编写以下代码。(控制台项目添加类库项目的程序集)static void Main(string[] args) { Assembly assembly = Assembly.GetAssembly(typeof(Class1)); Console.WriteLine("程序集名称:"+assembly.FullName); assembly.GetCustomAttributes(); Console.WriteLine("程序集Title:" + assembly.GetCustomAttribute<AssemblyTitleAttribute>().Title); Console.WriteLine("程序集描述:" + assembly.GetCustomAttribute<AssemblyDescriptionAttribute>().Description); Console.WriteLine("程序集公司:" + assembly.GetCustomAttribute<AssemblyCompanyAttribute>().Company); Console.Read(); }
其中Class1是类库项目的类。
执行后可以得到该类库项目输出的程序集的信息。例如:
在VS创建类库项目(以及其他的一些项目)时,会添加一个名为“AssemblyInfo.cs”的文件,其中内容如下:
using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("ClassLibrary1")] [assembly: AssemblyDescription("测试程序集")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("程序开发公司")] [assembly: AssemblyProduct("ClassLibrary1")] [assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 会使此程序集中的类型 //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 //请将此类型的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("e15c7334-bcfc-4c09-a0bc-3798f7742fc8")] // 程序集的版本信息由下列四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
这个文件并不是某个类文件,而是为程序集添加额外说明信息的文件,其中运用了很多特性——即使用"[]"包括起来的内容。
特性
特性可以可以为程序添加更多的元数据信息。上面的例子中,使用程序集特性为程序集class1添加了标题,程序集描述,创建公司,版权声明等等信息。除此之外,特性还可以用于如:
- 类
- 委托
- 枚举
- 事件
- 字段
- 泛型参数
- 接口
- 方法
- 属性
····
(可以参考AttributeTargets提供的选项)
特性可以作为一种特殊的类来对待,只是特性类统一继承于System.Attribute,使用特性时,通常可以不带Attribute后缀。
一个自定义特性的例子
一个普通的类继承System.Attribute之后,就成为了一个特性类。在命名类名通常会加上Attribute,例如:
[System.AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)] sealed class RequiredAttribute : Attribute { private bool flag; public RequiredAttribute(bool flag) { this.flag = flag; } public bool Flag { get { return flag; } } }
上面的特性Required标记了某个属性是必须的,并且通过AttributeTargets确定了只能用于属性字段。
可以看到:
特性类是密封类;
特性类也有构造器;
其中System.AttributeUsage可以用来限定自定义特性,(相当于特性的特性)使用特性修饰
如下代码,定义了一个类,并限定某些字段为必须的
public class FormClass { [Required(true)] public string ID { get; set; } [Required(true)] public string Name { get; set; } public string Birthday { get; set; } }
在网页提交表单是通常要做一些验证,常常就有检验字段是否为空的(当然,表单验证通常不会放在后台来做)。对于FormClass,验证其是否符合要求,可以用反射的方法获取所有的属性,获取属性上的特性标记,判断是否符合特性的要求。
public class Validator<T> { private T data; public Validator(T data){ this.data = data; } //是否已经验证 bool isValidate { get; set; } //验证方法 bool Validate() { PropertyInfo[] properties = data.GetType().GetProperties(); foreach (var item in properties) { RequiredAttribute required = item.GetCustomAttribute<RequiredAttribute>(); if (required!= null&&required.Flag == true && item.GetValue(data) == null) { return false; } else { continue; } } return true; } }
-
详谈C#特性及应用
2021-10-06 22:40:06详谈C#特性特性与反射息息相关,学习特性前,建议学习了解下这篇博客:C# 反射
一、特性直观感受
每一个特性都会带来对应得功能(其实这是个错觉)。
如下图,两个特性使我们经常在开发中见到的,Obsolete 可以影响到编译器发出警告,Serializable 可以序列化和反序列化影响程序运行
二、声明,定义
特性使用中括号声明,特性是一个类且必须直接或者间接继承 Attribute。
看了 Obsolete 与 Serializable 特性使用后,F12 查看一下源码,可以看到这两个特性都是一个类并且继承 Attribute
三、IL 语言特性
特性添加后,编译会在元素内部产生 IL,但我们没有办法直接使用的,而且在 metadata 里面会有记录。
这里定义了一个 CustomAttribute 特性,且声明到了 Product 类与其成员上,然后进行编译一下
使用反编译工具,在 bin 目录查看程序集(dll or exe),可以看到与我们写得 C# 源代码一样
点开 Product 类,切换到 IL 语言,可一看到其成员,且在 C# 源代码标记过的地方,都编译成了特性的构造函数
四、特性本身无意义
特性本身是没有用没有意义的。这句话,我们从程序运行也可以验证,下面是个一个简单的例子
这里定义了 CustomAttribute ,且在构造函数里面打印了一句话,在实例化对象是会进行提示
public class CustomAttribute: Attribute { public CustomAttribute() { Console.WriteLine("CustomAttribute 构造函数"); } }
再定义一个 Product 类,其被 CustomAttribute 声明
[Custom] public class Product { [Custom] public int Id { get; set; } [Custom] public int GetId() { return Id; } }
这里在 Main 函数,对类进行实例化,对属性赋值,且调用方法
static void Main(string[] args) { Product product= new Product() { Id=1}; Console.WriteLine(product.GetId()); Console.ReadKey(); }
启动,可以看到,程序正常运行,且没有打印 Custom 特性相关的提示信息,所以特性本身是没有意义的。
五、特性 API
特性本身是没有用的,程序运行过程中,我们能找到特性,而且也能应用一下。特性可以声明在类、方法、字段、属性、参数、返回值上,程序运行时可以获取到特性声明的信息。
首先定义 CustomAttribute 特性,且包含 Desc 属性和两个构造函数
public class CustomAttribute: Attribute { public string Desc { get; set; } public CustomAttribute() { Console.WriteLine("CustomAttribute 构造函数"); } public CustomAttribute(string desc) { Console.WriteLine("CustomAttribute 有参构造函数"); Desc = desc; } }
定义 Product 类,在其本身与成员及方法返回值全部声明 CustomAttribute
[Custom("我在类上")] public class Product { [Custom("我在字段上")] public string name; [Custom("我在属性上")] public string Name { get; set; } [Custom("我在方法上")] [return:Custom("我在返回值上")] public string GetName([Custom("我在参数上")] int id) { return Name; } }
在 Main 函数,写了一些特性实例化与获取信息的一些 API
static void Main(string[] args) { Product product= new Product() { }; Type type = typeof(Product); if (type.IsDefined(typeof(CustomAttribute), true)) { CustomAttribute customAttribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute),true); Console.WriteLine(customAttribute.Desc); } MethodInfo method = type.GetMethod("GetName"); if (method.IsDefined(typeof(CustomAttribute), true)) { CustomAttribute customAttribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute), true); Console.WriteLine(customAttribute.Desc); } ParameterInfo parameter = method.GetParameters()[0]; if (parameter.IsDefined(typeof(CustomAttribute), true)) { CustomAttribute customAttribute = (CustomAttribute)parameter.GetCustomAttributes(typeof(CustomAttribute), true)[0]; Console.WriteLine(customAttribute.Desc); } ParameterInfo returnParameter = method.ReturnParameter; if (returnParameter.IsDefined(typeof(CustomAttribute), true)) { CustomAttribute customAttribute = (CustomAttribute)returnParameter.GetCustomAttribute(typeof(CustomAttribute), true); Console.WriteLine(customAttribute.Desc); } PropertyInfo property = type.GetProperty("Name"); if (property.IsDefined(typeof(CustomAttribute), true)) { CustomAttribute customAttribute = (CustomAttribute)property.GetCustomAttribute(typeof(CustomAttribute), true); Console.WriteLine(customAttribute.Desc); } FieldInfo field = type.GetField("name"); if (field.IsDefined(typeof(CustomAttribute), true)) { CustomAttribute customAttribute = (CustomAttribute)field.GetCustomAttribute(typeof(CustomAttribute), true); Console.WriteLine(customAttribute.Desc); } Console.ReadKey(); }
启动,可以看到在成员运行时,拿取到声明特性的各种信息
CustomAttribute customAttribute = (CustomAttribute)field.GetCustomAttribute(typeof(CustomAttribute), true); // 会实例化特性
六、特性实际应用
没有破坏封装的前提下,可以额外的加点消息与行为。
说到这可能有的同学就问,上面说了那么多,在我们平时开发时有什么实际用处吗?答案是:有的,接下来使用实例进行讲解
6.1、添加额外消息
场景:分销系统,订单状态开发时通常喜欢使用枚举进行定义,但在 UI 展示其对于描述时有哪些做法呢?
首先,订单状态枚举定义,这里进行简化
public enum OrderStatus { /// <summary> /// 暂存 /// </summary> TemporaryStorage = 0, /// <summary> /// 未审批 /// </summary> NotApproved = 10, /// <summary> /// 已审批 /// </summary> Approved = 20 // 还有其他许多状态,这里不进行举例 }
第一种:手动维护,拼凑(还会有其他类似的写法,这里不进行一一举例)
列表, 这里有的开发时写了一个类似 K-V 的集合,分表对值与描述进行了,平凑返回给 UI ,进行渲染 select 或者 list
public static Dictionary<int, string> GetOrderStatusList() { Dictionary<int, string> orderStatus = new Dictionary<int, string>(); orderStatus.Add(OrderStatus.Approved.GetHashCode(), "暂存"); orderStatus.Add(OrderStatus.Approved.GetHashCode(), "未审批"); orderStatus.Add(OrderStatus.Approved.GetHashCode(), "已审批"); // 未来扩展状态流,都得手动进行添加 return orderStatus; }
表单,面对数据报表,单个状态对于的描述,通常使用一串的 if 进行获取
public static string GetOrderStatusRemark(OrderStatus orderStatus) { if (orderStatus == OrderStatus.Approved) { return "暂存"; } else if(orderStatus == OrderStatus.Approved) { return "已审批"; } else if (orderStatus == OrderStatus.NotApproved) { return "未审批"; } // 未来扩展状态流,都得手动进行添加 return orderStatus.ToString(); }
特性方式:提高开发效率,且公用,易于维护,易于编码
首先定义一个 RemarkAttribute 特性,使用 _Remark 承载我们的描述信息
public class RemarkAttribute : Attribute { private string _Remark { get; set; } public RemarkAttribute(string remark) { _Remark = remark; } public string GetRemark() { return _Remark; } }
在状态枚举成员打上特性,且输入每个状态的描述信息
public enum OrderStatus { /// <summary> /// 暂存 /// </summary> [Remark("暂存")] TemporaryStorage = 0, /// <summary> /// 未审批 /// </summary> [Remark("未审批")] NotApproved = 10, /// <summary> /// 已审批 /// </summary> [Remark("已审批")] Approved = 20 // 还有其他许多状态,这里不进行举例 }
创建一个状态枚举的扩张类,写一个 GetRemark 扩张方法,用于取当前状态成员标记特性的描述信息
public static class OrderStatusExtension { public static string GetRemark(this OrderStatus val) { Type type = val.GetType(); FieldInfo field = type.GetField(val.ToString()); if (field.IsDefined(typeof(RemarkAttribute),true)) { RemarkAttribute remarkAttribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true); return remarkAttribute.GetRemark(); } return val.ToString(); } }
启动,可以看到,成功从 Remark 特性成员 _Remark 字段获取到 NotApproved 状态的描述信息
static void Main(string[] args) { OrderStatus orderStatus = OrderStatus.NotApproved; Console.WriteLine(orderStatus.GetRemark()); Console.ReadKey(); }
延展:平时开发程序,model 对于的表格或者表单的描述也可用此方法6.2、添加额外行为
场景:表单提交,数据效验
public class Personal { public string Name { get; set; } public string Address { get; set; } public long QQ { get; set; } // 还有许多其他字段 }
第一种:这种写法最简单的,通常在 mvc 的 action 方法里面进行效验,在多个 action 中这种会造成代码重复,且维护困难
Personal personal= new Personal() { QQ = 1}; if (personal.QQ > 10001 && personal.QQ < 999999999999) { } // 还有更多 else if () 对其他进行效验
第二种:由于第一种产生的问题,有的小伙伴会将验证逻辑放到 get set 访问器中,这种造成了职责不分明,实体本身是承载信息的,不需要存在业务逻辑
public class Personal { public string Name { get; set; } public string Address { get; set; } public long QQ { get { return this.QQ; } set { if (value > 10001 && value < 999999999999) { this.QQ = value; } else { throw new Exception("QQ号不合法"); } } } // 还有许多其他字段 }
特性方式:由于以上两种产生的问题,我们可以使用特性进行处理,易于维护、易于编码、易于公用
定义一个 LongAttribute 特性,其中有个最小、最大字段,且有一个 Validate 方法用于处理效验逻辑
public class LongAttribute : Attribute { private long _Min { get; set; } private long _Max { get; set; } public LongAttribute(long min,long max) { _Min = min; _Max = max; } public bool Validate(object val) { if (val != null && !string.IsNullOrEmpty(val.ToString())) { if (long.TryParse(val.ToString(),out long lResult)) { if (lResult > _Min && lResult < _Max) { return true; } } } return false; } }
定义一个实体类,有 Name、Address 、QQ 三个字段(实际比这多),在 QQ 字段标记特性 Long 并定义 min max
public class Personal { public string Name { get; set; } public string Address { get; set; } [Long(1001, 99999999999)] public long QQ { get; set; } // 还有许多其他字段 }
定义一个 Validate 的扩张类与扩张方法,其作用是取得对象 Type 获取 LongAttribute 并调用其 Validate 进行业务逻辑效验
public static class ValidateExtension { public static bool Validate(this object val) { Type type= val.GetType(); foreach (var prop in type.GetProperties()) { if (prop.IsDefined(typeof(LongAttribute),true)) { LongAttribute longAttribute = (LongAttribute)prop.GetCustomAttribute(typeof(LongAttribute),true); if(!longAttribute.Validate(prop.GetValue(val))) { return false; } } } return true; } }
这里,分别实例化两个 Personal ,并对 QQ 字段赋予不合法与合法的值
static void Main(string[] args) { Personal personal= new Personal() { QQ = 1}; Console.WriteLine(personal.Validate()); Personal personal1= new Personal() { QQ = 100000}; Console.WriteLine(personal1.Validate()); Console.ReadKey(); }
启动,可以看到对效验的结果,分别返回了 false true 成功的进行了效验
有人要问只验证个 QQ 有啥意义?且慢,我们还可以多名字等其他字段进行验证,接着我们对 name 字段进行效验
定义一个 LengthAttribute 特性,定义 min max 两个成员,Validate 方法进行业务逻辑效验
public class LengthAttribute : Attribute { private long _Min { get; set; } private long _Max { get; set; } public LengthAttribute(long min, long max) { _Min = min; _Max = max; } public bool Validate(object val) { if (val != null && !string.IsNullOrEmpty(val.ToString())) { if (val.ToString().Length > _Min && val.ToString().Length < _Max) { return true; } } return false; } }
在 name 字段打上 Length 特性,且定义 min = 1,max =10
public class Personal { [Length(1, 10)] public string Name { get; set; } public string Address { get; set; } [Long(1001, 99999999999)] public long QQ { get; set; } // 还有许多其他字段 }
在 ValidateExtension 类 Validate 方法添加一个 LengthAttribute 特性的检查
public static class ValidateExtension { public static bool Validate(this object val) { Type type= val.GetType(); foreach (var prop in type.GetProperties()) { if (prop.IsDefined(typeof(LongAttribute),true)) { LongAttribute longAttribute = (LongAttribute)prop.GetCustomAttribute(typeof(LongAttribute),true); if(!longAttribute.Validate(prop.GetValue(val))) { return false; } } if (prop.IsDefined(typeof(LengthAttribute), true)) { LengthAttribute longAttribute = (LengthAttribute)prop.GetCustomAttribute(typeof(LengthAttribute), true); if(!longAttribute.Validate(prop.GetValue(val))) { return false; } } } return true; } }
实例化 Personal ,对 name 字段进行赋值
static void Main(string[] args) { Personal personal= new Personal() { Name = "张三"}; Console.WriteLine(personal.Validate()); Personal personal1= new Personal() { Name = "张"}; Console.WriteLine(personal1.Validate()); Console.ReadKey(); }
启动,可以看到,根据标记的特性正确的进行了检测
抽象public class AbstractValidateAttribute : Attribute { public virtual bool Validate(object val) => false; }
public class LongAttribute : AbstractValidateAttribute { private long _Min { get; set; } private long _Max { get; set; } public LongAttribute(long min, long max) { _Min = min; _Max = max; } public override bool Validate(object val) { if (val != null && !string.IsNullOrEmpty(val.ToString())) { if (long.TryParse(val.ToString(), out long lResult)) { if (lResult > _Min && lResult < _Max) { return true; } } } return false; } } public class LengthAttribute : AbstractValidateAttribute { private long _Min { get; set; } private long _Max { get; set; } public LengthAttribute(long min, long max) { _Min = min; _Max = max; } public override bool Validate(object val) { if (val != null && !string.IsNullOrEmpty(val.ToString())) { if (val.ToString().Length > _Min && val.ToString().Length < _Max) { return true; } } return false; } }
static void Main(string[] args) { Personal personal= new Personal() { Name = "张",QQ = 9999999}; Console.WriteLine(personal.Validate()); Personal personal1 = new Personal() { Name = "张三", QQ = 1 }; Console.WriteLine(personal1.Validate()); Personal personal2 = new Personal() { Name = "张三", QQ = 9999999 }; Console.WriteLine(personal2.Validate()); Console.ReadKey(); }
-
C#特性源码案例1 c#经典案例.txt
2022-05-07 22:00:43C#特性源码案例1 c#经典案例.txt -
C# 特性的基本用法
2020-07-06 00:58:48特性是一种允许我们向程序的程序集增加元数据的语言结构,它用于保存程序结构信息的某种特殊类型的类。简单的说,特性主要是结合反射来获得程序的某些信息,在一般情况下其实没什么卵用。 特性的使用形式如下: ...目录
3.CallerFilePath、CallerLineNumber、CallerMemberName
前言
特性是一种允许我们向程序的程序集增加元数据的语言结构,它用于保存程序结构信息的某种特殊类型的类。简单的说,特性主要是结合反射来获得程序的某些信息,在一般情况下其实没什么卵用。
特性的使用形式如下:
[Serializable] //特性 public class Myclass { ... } [MyAttribute("Simple class","Version 3.57")] //带有参数的特性 public class MyOtherClass { ... }
一.预定义的保留特性
1.Obsolete特性
Obsolete特性将程序结构标注为过期的,但是标注的程序依然可以使用,只是用于警告用户尽量使用新的方法。
using System; namespace AttributeTest { class Program { static void Main(string[] args) { PrintOut("Hello World!"); } [Obsolete("这个方法要过期了,请使用SuperPrintOut方法")] static void PrintOut(string str) { Console.WriteLine(str); } static void SuperPrintOut(string str) { Console.WriteLine(str ?? "这是一个空字符串!"); } } }
在Vs中 被[Obsolete]修饰的方法还会给出提示:
在编译的时候也会给出类似的警告。
2.Conditional特性
Conditional特性类似于条件调用,如果熟悉#if、#endif的话,应该还是很好理解的。不同的是Conditional是对方法本省的修饰,而#if、#endif则是对调用的修饰。看下面的例子:
#define SABER using System; using System.Diagnostics; namespace AttributeTest { class Program { static void Main(string[] args) { #if TEST PrintOut("Hello World!"); #endif SuperPrintOut(null); } [Obsolete("这个方法要过期了,请使用SuperPrintOut方法")] static void PrintOut(string str) { Console.WriteLine(str); } [Conditional("SABER")] static void SuperPrintOut(string str) { Console.WriteLine(str ?? "这是一个空字符串!"); } } }
运行结果如下:
由于我没有预先定义TEST 字符串,因此PrintOut函数不会执行,并且在VS编辑器中
PrintOut("Hello World!"); //实际显示为灰色
显示为灰色,很明显的告诉我们这一行不执行。而我定义了SABER,因此会执行SuperPrintOut函数。
加入你把SABER的定义去掉,在Main函数中不会有任何变化,但是运行结果不会再出现上面的那行字符串。
因为[Conditional]主要对方法进行修饰,因此要对方法进行条件调用的时候,建议用[Conditional]修饰而不是#if。且Conditional只能修饰返回值为Void类型的方法。
3.CallerFilePath、CallerLineNumber、CallerMemberName
这三个特性只能用于方法中的可选参数,分别提醒文件路径、代码所在行数、调用成员名称信息。具体的使用方法请参见我之前的博客:C# CallerMemberName特性介绍以及简化InotifyPropertyChanged的实现。
4.DebuggerStepThough
如果你很确定你的某段代码是正确的,在调试是,对其反复单步调试只会徒增时间,那么你可以使用这个特性。该特性是的在单步调试时,不要进入某些方法。
class SimpleA { int _x = 1; int X { get => _x; [DebuggerStepThrough] //不进入set访问器 set { _x = _x * 2; _x += value; } } public int Y { get; set; } [DebuggerStepThrough] //不进入这个方法 void IncrementFields() { X++; Y++; } }
4.其它预定义特性
.Net框架预定义了很多编译器和CLR能理解和解释的特性,下面给出了一些重要的特性:
一直字段也可以同事被应用多个特性,下面这两种特性使用时等价的:
二.自定义特性
其实特性只是某个特殊类型的类。要申明一个自定义特性需要:
-
声明一个派生自System.Attribute的类
-
给它取一个以后缀Attribute结尾的名字
下面时一个特性的声明和使用:
[MyAttribyte("An simple class",reviewe:"QYL",ver:"2.0")] class SimpleA { int _x = 1; int X { get => _x; [DebuggerStepThrough] //不进入set访问器 set { _x = _x * 2; _x += value; } } public int Y { get; set; } [DebuggerStepThrough] //不进入这个方法 void IncrementFields() { X++; Y++; } } public sealed class MyAttribyteAttribute:System.Attribute { public string Description; public string Version; public string Reviewer; public MyAttribyteAttribute(string desc) { Description = desc; } public MyAttribyteAttribute(string desc,string ver,string reviewe) { Description = desc; Reviewer = reviewe; Version = ver; } }
可以使用AttributeUsage特性来限制自定义特性只能使用在某些目标上,例如一旦限制特性使用在方法上,就不能像上面那样修饰类:
AttributeUsage有三个重要的公共属性:
下面这个特性表示:MyAttribute只能用于类上、MyAttribute不会应用到它的派生类上、不能有MyAttibute的多个实例应用到同一个目标上。
[AttributeUsage(AttributeTargets.Class, //必须的,位置参数 Inherited =false, //可选的,命名参数)] AllowMultiple =false)] //可选的,命名参数
三.访问特性
我们可以使用Type对象来获取类型信息,对于特性,Type也有2个重要的方法:IsDefined和GetCustomeAttributes。
IsDefined方法用来检测某个特性是否应用到了某个类上。
#define SABER using System; using System.Diagnostics; namespace AttributeTest { class Program { static void Main(string[] args) { SimpleA simpleA = new SimpleA(); Type t = simpleA.GetType(); //从实例中获取类型对象 bool isDefined = t.IsDefined(typeof(MyAttribyteAttribute), false); if (isDefined) Console.WriteLine($"MyAttribute is applied to type {t.Name}"); #if TEST PrintOut("Hello World!"); #endif SuperPrintOut(null); } } class SimpleA { int _x = 1; int X { get => _x; [DebuggerStepThrough] //不进入set访问器 set { _x = _x * 2; _x += value; } } public int Y { get; set; } [DebuggerStepThrough] //不进入这个方法 void IncrementFields() { X++; Y++; } } [AttributeUsage(AttributeTargets.Class, //必须的,位置参数 Inherited =false, //可选的,命名参数)] AllowMultiple =false)] //可选的,命名参数 public sealed class MyAttribyteAttribute:System.Attribute { public string Description; public string Version; public string Reviewer; public MyAttribyteAttribute(string desc) { Description = desc; } public MyAttribyteAttribute(string desc,string ver,string reviewe) { Description = desc; Reviewer = reviewe; Version = ver; } } }
输出:MyAttribute is applied to type SimpleA
GetCustomeAttributes方法返回应用到结构的数组。实际返回的对象是object的数组,因此我们必须将他强制转换为相应的特性类型。布尔参数指定是否继续搜索继承树来查找特性。调用该方法后,每一个与目标相关联的特性的实例都会被创建
object[] attrs=t.GetCustomedAttributes(false)
对Main函数作修改,使用GetCustomedAttributes函数的示例:
static void Main(string[] args) { SimpleA simpleA = new SimpleA(); Type t = simpleA.GetType(); //从实例中获取类型对象 bool isDefined = t.IsDefined(typeof(MyAttributeAttribute), false); //if (isDefined) // Console.WriteLine($"MyAttribute is applied to type {t.Name}"); object[] attrs = t.GetCustomAttributes(false); foreach(var a in attrs) { if (a is MyAttributeAttribute myAttribyteAttribute) { Console.WriteLine($"Description: {myAttribyteAttribute.Description}"); Console.WriteLine($"Version number: {myAttribyteAttribute.Version}"); Console.WriteLine($"Reviewer ID: {myAttribyteAttribute.Reviewer}"); } } #if TEST PrintOut("Hello World!"); #endif SuperPrintOut(null); }
运行结果:
Description: An simple class Version number: 2.0 Reviewer ID: QYL 这是一个空字符串!
-
-
C#特性(Attribute)讲解
2022-01-07 16:33:111.特性的介绍与使用 特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所... -
C#特性完整版实例代码
2019-07-07 14:28:33C#特性完整详解文档附加实例代码,100能正常运行含注释,有问题可留言! -
C# 特性(Attribute).docx
2021-02-03 13:26:24预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型 -
发掘C#特性赋予科学计算项目以威力
2021-02-04 14:37:16他还论述了C#数据类型,包括数组和矩阵,及其它在科学计算应用中起重要作用的语言特性。 C#语言已获得工作在不同领域开发者的尊敬并在他们中间得到相当的普及。最近两年,C#在交付健壮的产品中起着重要的作用,从... -
C# 特性详解
2018-06-23 08:37:46C# 特性详解 1、什么是特性 1)属性与特性的区别 属性(Property):属性是面向对象思想里所说的封装在类里面的数据字段,Get,Set方法。 特性(Attribute): 官方解释:特性是给指定的某一声明的一则附加的... -
c#特性 同过特性获取类中的属性,获取属性对应的特性值
2020-08-19 10:53:43//获取特性值 string tableName = string.Empty; List<string> listColumnName = new List<string>(); Type objType = typeof(T_In_ReceiptDetail); //取属性上的自定义特性 foreach (PropertyInfo... -
Unity / C# 特性的使用方法
2021-01-28 19:07:28必须有该类型的组件才可以使用该脚本,加入该脚本的同时智能添加一个该类型的组件 [RequireComponent(typeof(MeshFilter))] 添加备注到下面第一个public变量在细节面板的前面 [Header("备注")] ... -
愿意把java白皮书的11个特性改成C#特性归类.pdf
2022-01-25 23:20:25愿意把java白皮书的11个特性改成C#特性归类.pdf -
(2)C# 基础——C#特性
2018-08-10 15:13:30.NET C# Web开发学习之路——C#特性 C#历史办版本及特性 语言版本 发布时间 .NET Framework要求 Visual版本 C# 1.0 2002.1 .NET Framework 1.0 Visual Studio .NET 2002 C# 1.1\1.2 2003.4 ... -
C# 特性详解(Attribute)
2018-10-26 22:48:32参考《C#高级编程》第10版 0X01 特性(Attribute) 特性定义 特性不会影响编译过程,因为编译器不能识别它们,但这些特性在应用于程序元素时,可以在编译好的程序集中用作元数据 上面这句话是书上说的,但不太认可... -
KeyAttribute Key c# 特性
2019-09-20 17:53:48转载于:https://www.cnblogs.com/MyFlora/archive/2012/08/29/2661968.html -
C#特性类的使用
2018-10-10 15:10:24特性类的使用过程: 第一步:定义一个特性类,定义一些成员来包含验证时需要的数据; 第二步:创建特性类实例; 创建一个特性类的实例,里面包含着验证某一个属性或者字段需要的数据。 将该实例关联到某个属性上面... -
C# 特性参数(注解属性加在参数前面)
2019-07-23 19:44:39C# 特性参数(注解属性加在参数前面) 特性参数 webapi 框架里有很多特性参数,为了解除一些新人的疑惑,写个小例子分享下。 class Program { static void Main(string[] args) { var message = new ... -
C# 特性Description的值的获取
2020-01-20 13:53:15public class dataE{ /// <summary> /// 企业名称 /// </summary> /// <returns></returns> [Description("企业名称")] public string Ent... -
JAVA注解和C#特性
2017-11-02 18:04:01C#: Attribute:特性(HTML标签叫属性) Property:属性 Usage:使用、用法、习惯、惯例 AttributeUsage:特性用法(个人翻译) Obsolete:过期 (C#中代表过期,不推荐使用) Conditional:条件JAVA: Annotation:注释 ... -
C#特性——Description
2019-03-07 11:16:36一般来说,我们给枚举注释这样。...这就用到Description特性,原理不知道,我也刚学,先学会用吧。 先写个枚举 /// <summary> /// 季节 /// </summary> public enum Season { ... -
C#特性
2015-01-21 16:38:18C#特性学习笔记 特性标签的本质: 1>特性是一个类,这个类的声明我们有一个规范,即:以Attribute结尾,例如MyClassAttribute 。当着仅仅是一个规范,如果你是在不以Attribute结尾也没关系。 2>这个类一定... -
C# 特性(Attributes)
2017-02-28 17:00:13特性与程序实体相关联后,即可在运行时用反射技术查询特性。 例如,在一个方法前标注[Obsolete]特性,则调用该方法时VS则会提示该方法已过期的警告,如下图: 又如,在.Net Remoting的远程对象中,如果要... -
C# 10个常用特性汇总
2020-08-18 21:34:55主要介绍了C# 10个常用特性,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下 -
C#的特性
2022-03-20 15:00:53C#的特性就是可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。 将特性与程序实体相关联后,可以在运行时使用反射这项技术查询特性。特性也是用来给代码添加额外信息的一种手段,我们... -
C# 特性(Attribute)详细介绍
2019-03-01 17:05:48我们将会在Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。 using System; [AttributeUsage(AttributeTargets.Class), AllowMultiple = false , Inherited =... -
C#之特性
2022-01-24 22:49:00一、什么是特性? 特性是一种允许我们向程序的程序集添加元数据的语言结构。它是用于保存程序结构信息的特殊类型的类。 或者说 特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、... -
C#特性与枚举值遍历
2011-09-05 02:22:05利用反射技术遍历枚举列表,利用特性获取枚举描述,简单实现枚举键值字典