【C#学习笔记】【Attribute】

C#学习笔记系列

第一章 【C#学习笔记】【StackTrace】
第二章 【C#学习笔记】【Attribute】



前言

学习特性的使用,包括对.Net自带的三种预定义特性:Obsolete、Conditional、AttributeUsag的详细讲解,也列举了常用的特性,其中包括控件设计时要用到的一系列特性,最后介绍了如何进行自定义特性(仅以枚举作为示例)。


一、Attribute介绍

Attribute(特性)
MSDN给出定义:
Attribute类将预定义的系统信息或用户定义的自定义信息与目标元素相关联。
目标元素可以是程序集、类、构造函数、委托、枚举、事件、字段、接口、方法、可移植可执行文件模块、参数、属性(Property)、返回值、结构或其他特性(Attribute)。

二、Obsolete

Obsolete是.Net自带的三种预定义特性之一,该特性可以用来声明程序体是过时的,一个程序在不断迭代更新的过程中,总会出现要用新方法代替老方法的情况,但又不能删除老方法因为旧的项目在用,这是我们使用这个特性就能起到提示作用,Obsolete特性将程序结构标注为被弃用的。
在这里插入图片描述Obsolete有三个重载

public sealed class ObsoleteAttribute : Attribute
{
    //表示该程序体是被弃用的
    public ObsoleteAttribute();
    //string参数可以输入提示内容,在鼠标移动到程序体名称上显示,默认为“已过时”
    public ObsoleteAttribute(string? message);
    //bool参数用来表示该弃用方法是否编译报警,若为true,被使用时,会产生编译器错误,无法编译成功;若为false,则只是编译警告,告知方法是弃用的,但仍可编译成功并运行
    public ObsoleteAttribute(string? message, bool error);
}

三、Conditional

Conditional是.Net自带的三种预定义特性之一,位于System.Diagnostics命名空间下,这个特定通过条件判断决定是否运行下面对应的程序体
在这里插入图片描述

可以看到,定义了“Debug”字符串,方法Test1会运行,未定义“Release”字符串,Test2不会被运行。

四、AttributeUsage

AttributeUsag是.Net自带的三种预定义特性之一,仅对继承自Attribute的派生类有效,目的是控制当前类的使用范围。
如下面代码,定义了一个AttributeUsage的自定义特性,在AttributeUsage参数中传入了AttributeTargets.Method,指定当前特性仅对方法有效。

[AttributeUsage(AttributeTargets.Method)]
public sealed class FlowDemoAttribute : Attribute
{
    public FlowDemoAttribute() { }

    public string? Name { get; set; }

    public string? GetDisplayName()
    {
        return Name;
    }
}

那么在使用这个特性的时候,就只能定义在方法的上面,若放到其他的机构如字段,就会报错,如下图所示
在这里插入图片描述在这里插入图片描述如果需要指定对多个程序体有效的话,这里写多个目标,中间用 | 隔开,或者指定AttributeUsage.All,表示对所有程序体有效

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]

AttributeUsage有两个重载

public sealed class AttributeUsageAttribute: Attribute
{
    //AttributeTargets参数规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合,默认为“AttributeUsage.All”
    public AttributeUsageAttribute(AttributeTargets validOn);
    //allowMultiple参数,如果为 true,则该特性是多用的。默认值是 false(单用的)
    //inherited参数,如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)
    public AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited);
}

AttributeTargets是一个枚举,它包括以下内容:

namespace System
{
    //
    // 摘要:
    //     Specifies the application elements on which it is valid to apply an attribute.
    [Flags]
    public enum AttributeTargets
    {
        //
        // 摘要:
        //     Attribute can be applied to an assembly.
        Assembly = 1,
        //
        // 摘要:
        //     Attribute can be applied to a module. Module refers to a portable executable
        //     file (.dll or.exe) and not a Visual Basic standard module.
        Module = 2,
        //
        // 摘要:
        //     Attribute can be applied to a class.
        Class = 4,
        //
        // 摘要:
        //     Attribute can be applied to a structure; that is, a value type.
        Struct = 8,
        //
        // 摘要:
        //     Attribute can be applied to an enumeration.
        Enum = 16,
        //
        // 摘要:
        //     Attribute can be applied to a constructor.
        Constructor = 32,
        //
        // 摘要:
        //     Attribute can be applied to a method.
        Method = 64,
        //
        // 摘要:
        //     Attribute can be applied to a property.
        Property = 128,
        //
        // 摘要:
        //     Attribute can be applied to a field.
        Field = 256,
        //
        // 摘要:
        //     Attribute can be applied to an event.
        Event = 512,
        //
        // 摘要:
        //     Attribute can be applied to an interface.
        Interface = 1024,
        //
        // 摘要:
        //     Attribute can be applied to a parameter.
        Parameter = 2048,
        //
        // 摘要:
        //     Attribute can be applied to a delegate.
        Delegate = 4096,
        //
        // 摘要:
        //     Attribute can be applied to a return value.
        ReturnValue = 8192,
        //
        // 摘要:
        //     Attribute can be applied to a generic parameter. Currently, this attribute can
        //     be applied only in C#, Microsoft intermediate language (MSIL), and emitted code.
        GenericParameter = 16384,
        //
        // 摘要:
        //     Attribute can be applied to any application element.
        All = 32767
    }

五、常用的特性

在.Net系统库中有很多定义好的特性,接下来挑部分常用的特性做简单介绍

FlagsAttribute
作用域:Enum
说明:指示可以将枚举作为位域(一组标志)处理,即两个枚举可以进行与(&)或(|)运算(二进制形式),通过HasFlag()判断是否包含。
详细说明:
使用[Flags]的场景:枚举的类型是不唯一的,比如颜色,一张图片可能同时包含枚举中的几种颜色,通过EColorValue.HasFlag(EColor.yellow);判断是否包含,或者通过"|“”&"来作计算;
不使用[Flags]的场景:枚举的类型是唯一的,比如用户权限,要么是操作员,要么是管理员。

SerializableAttribute
作用域:Class,Struct,Rnum,Delegate
说明:指示一个类可以序列化。

ComVisibleAttribute
作用域:Assembly,Class,Struct,Enum,Method,Propery,Field,Interface,Delegate
说明:控制程序集中个别托管类型、成员或所有类型对COM的可访问性。

DisplayAttribute
作用域:Method,Property,Field,Parameter
说明:提供一个通用特性,使您可以为实体分部类的类型和成员指定可本地化的字符串。

控件设计中会用到的特性:

BrowsableAttribute
作用域:All
说明:指定一个属性或事件是否应显示在“属性”窗口中。
CategoryAttribute
作用域:All
说明:指定当属性或事件显示在一个设置为“按分类顺序”模式的System.Windows.Forms.PropertyGrid控件中,用于给属性或事件分组的类别的名称。
DescriptionAttribute
作用域:All
说明:指定属性或事件的说明。
BindableAttribute
作用域:All
说明:指定成员是否通常用于绑定。无法继承此类。
DefaultPropertyAttribute
作用域:Class
说明:指定组件的默认属性。
DefaultValueAttribute
作用域:All
说明:指定属性的默认值。
EditorAttribute
作用域:All
说明:指定用来更改属性的编辑器,无法继承此类。
LocalizableAttribute
作用域:All
说明:指定属性是否应本地化,无法继承此类。
DesignerSerializationVisibilityAttribute
作用域:Method,Property,Field,Event
说明:指定在设计时序列化组件上的属性时所使用的持久性类型。
TypeConverterAttribute
作用域:All
说明:指定用作此特性所绑定到的对象的转换器的类型,无法继承此类。
DefaultEventAttribute
作用域:Class
说明:指定组件的默认事件。

六、自定义特性

.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。

创建并使用自定义特性包含四个步骤:

  1. 声明自定义特性
  2. 构建自定义特性
  3. 在目标程序元素上应用自定义特性
  4. 通过反射访问特性

6.1. 声明自定义特性

一个新的自定义特性应派生自 System.Attribute 类,我们声明一个名为FlowCtrlAttribute的自定义特性:

[AttributeUsage(AttributeTargets.Field)]
public sealed class FlowCtrlAttribute : Attribute
{
    //TODO..
}

6.2. 构建自定义特性

让我们构建一个名为 FlowCtrlAttribute 的自定义特性,该特性将存储流程图中控件的类型名称、宽高大小信息。它存储下面的信息:

  • 流程图控件类型名称
  • 流程图控件默认宽度大小
  • 流程图控件默认高度大小

下面的代码演示了 FlowCtrlAttribute 类:

[AttributeUsage(AttributeTargets.Field)]
public sealed class FlowCtrlAttribute : Attribute
{
    /// <summary>
    /// 流程图控件类型名称
    /// </summary>
    public string? TypeName { get; set; }
    /// <summary>
    /// 流程图控件默认宽度大小
    /// </summary>
    public double Width { get; set; }
    /// <summary>
    /// 流程图控件默认高度大小
    /// </summary>
    public double Height { get; set; }

    public FlowCtrlAttribute() { }
    public FlowCtrlAttribute(string typeName, double width, double height)
    {
        this.TypeName = typeName;
        this.Width = width;
        this.Height = height;
    }

    public string? GetTypeName()
    {
        return TypeName;
    }
    public double GetWidth()
    {
        return Width;
    }
    public double GetHeight()
    {
        return Height;
    }
}

6.3. 在目标程序元素上应用自定义特性

通过把特性放置在紧接着它的目标之前,来应用该特性,我们这里应用于枚举的场景:

public enum EFlowControl
{
    [FlowCtrl(TypeName = "开始", Width = 80, Height = 30)]
    StartControl,
    [FlowCtrl(TypeName = "结束", Width = 80, Height = 30)]
    FinishControl,
    [FlowCtrl(TypeName = "行为", Width = 80, Height = 30)]
    ActionControl,
    [FlowCtrl(TypeName = "条件", Width = 80, Height = 60)]
    ConditionControl,
    [FlowCtrl(TypeName = "文件", Width = 80, Height = 40)]
    FileControl,
    [FlowCtrl(TypeName = "模块", Width = 80, Height = 40)]
    ModuleControl,
    [FlowCtrl(TypeName = "预设模块", Width = 100, Height = 40)]
    DefineModuleControl,
}

6.4. 通过反射访问特性

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

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优缺点
优点:

  • 反射提高了程序的灵活性和扩展性。
  • 降低耦合性,提高自适应能力。
  • 它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

  • 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
  • 使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

反射(Reflection)的用途
反射(Reflection)有下列用途:

  • 它允许在运行时查看特性(attribute)信息。
  • 它允许审查集合中的各种类型,以及实例化这些类型。
  • 它允许延迟绑定的方法和属性(property)。
  • 它允许在运行时创建新类型,然后使用这些类型执行一些任务。

通过反射获取枚举的特性中存储的数据:

public static class EumHelper
{
    /// <summary>
    /// 获得枚举的TypeName
    /// </summary>
    /// <param name="eum">枚举</param>
    /// <returns>TypeName</returns>
    public static string GetTypeName<T>(this Enum eum)
    {
        Type type = eum.GetType();//先获取这个枚举的类型
        FieldInfo? field = type.GetField(eum.ToString());//通过这个类型获取到值
        FlowCtrlAttribute? obj = (FlowCtrlAttribute?)field?.GetCustomAttribute(typeof(FlowCtrlAttribute));//得到特性
        return obj?.GetTypeName() ?? string.Empty;
    }

    /// <summary>
    /// 获得枚举的Width
    /// </summary>
    /// <param name="eum">枚举</param>
    /// <returns>Width</returns>
    public static double GetWidth(this Enum eum)
    {
        Type type = eum.GetType();//先获取这个枚举的类型
        FieldInfo? field = type.GetField(eum.ToString());//通过这个类型获取到值
        FlowCtrlAttribute? obj = (FlowCtrlAttribute?)field?.GetCustomAttribute(typeof(FlowCtrlAttribute));//得到特性
        return obj?.GetWidth() ?? double.NaN;
    }

    /// <summary>
    /// 获得枚举的Height
    /// </summary>
    /// <param name="eum">枚举</param>
    /// <returns>Height</returns>
    public static double GetHeight(this Enum eum)
    {
        Type type = eum.GetType();//先获取这个枚举的类型
        FieldInfo? field = type.GetField(eum.ToString());//通过这个类型获取到值
        FlowCtrlAttribute? obj = (FlowCtrlAttribute?)field?.GetCustomAttribute(typeof(FlowCtrlAttribute));//得到特性
        return obj?.GetHeight() ?? double.NaN;
    }
}

总结

不积硅步,何以致千里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值