C#中的Attribte(特性)和Reflection(反射)

385 阅读3分钟

C#中的Attribte(特性)

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。 特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

  1. 一个自定义特性类必须直接或间接继承自System.Attribute特性类。
  2. 为该自定义特性类指定System.AttributeUsage特性,并指定限制参数(枚举System.AttributeTargets和可选的AllowMultiple、Inherited命名参数)。AttributeUsage的命名参数:AllowMultiple表示是否允许多次使用在同一目标上、Inherited表示是否同时应用于派生类型或重载版本。
  3. 类名是特性的名称。
  4. 构造函数的参数是自定义特性的定位参数(应用该特性时必须放在参数列表的最前面),也可以是无参构造函数(如[Serializable])。
  5. 任何公共的读写字段或属性都是可选的命名参数。
  6. 如果特性类包含一个属性,则该属性必须为读写属性。

C#中的Reflection(反射)

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
你可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优点

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

缺点

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

反射(Reflection)的用途

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

案例

    /// <summary>
    /// 表达 该属性 是否为用户输入的,还是从之前的模板带出来的
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class PropertyIsUserInputAttribute : Attribute
    {
        public bool IsUserInput;

        public PropertyIsUserInputAttribute(bool isUserInput)
        {
            IsUserInput = isUserInput;
        }
    }

    // 使用
    public class TestDataModel
    {
        [PropertyExcelColumnIndex("B")]
        [PropertyIsUserInput(false)]
        public string 名称 { get; set; }

        [PropertyExcelColumnIndex("C")]
        [PropertyIsUserInput(true)]
        public string 备注 { get; set; }
    }
    
    // 通过反射取特性的信息
    public static int GetPropertyExcelColumnIndexValue(this BaseSheetModel obj, string propertyName)
    {
        var referencedType = obj.GetType();
        int mappingColumnIndex = -1;
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(referencedType);
        if (properties != null)
        {
            PropertyDescriptor property = properties[propertyName];
            if (property != null)
            {
                AttributeCollection attributeCollection = property.Attributes;
                PropertyExcelColumnIndexAttribute mappingAttribute = (PropertyExcelColumnIndexAttribute)attributeCollection[typeof(PropertyExcelColumnIndexAttribute)];
                if (mappingAttribute == null)
                {
                    return mappingColumnIndex;
                }
                mappingColumnIndex = ExcelFunction.ToIndex(mappingAttribute.ExcelColumnName);
            }
        }
        if (mappingColumnIndex < 0)
        {
            throw new Exception($"没有找到{propertyName}的Excel映射位置。");
        }
        return mappingColumnIndex;
    }

参考

  1. www.runoob.com/csharp/csha…
  2. www.runoob.com/csharp/csha…