还记得特性和反射吗(一)

Attribute,特性,是可以为代码提供额外信息的方式,同时也可以为代码标记信息并由外部读取,以此影响被“修饰(decorating)”的代码的使用方式。

一些例子:

Attribute 通过 System.Attribute 类进行派生,要注意的是

  1. 要将定义的特性应用到什么类型的目标(类,属性或其他)
  2. 是否可以对同一目标多次应用。

另外

  1. 在特性应用到类或接口上时,是否由派生类和接口继承,
  2. 必选和可选参数

而这些也是通过应用特性来进行配置:AttributeUsageAttribute,其中 AttributeTargets 是一个枚举类型,用来指定可以应用到哪些程序元素上,AllowMultiple 指定是否可以多次应用,Inherited 参数表示是否自动应用到派生类或接口上。

在《C# 入门经典(第7版)》中的例子中,仅仅表现了如何通过 Type.GetCustomAttributes() 方法来获取特性并使用其信息。

[AttributeUsage(AttributeTarget.Class|AttributeTarget.Method, AllowMultiple = false)]
class DoesInterestingThingsAttribute : Attribute
{
    // 传递参数给特性的构造函数
    public DoesInterestingThingsAttribute(int howManyTimes)
    {
        HowManyTimes = howManyTimes; 
    }
    // 定义了特性的可选参数,在使用时虽然被"()"包含,但不会传递给构造函数,会查找有该名称的公共属性或字段
    public string WhatDoesItDo {get;set;}
    public int HowManyTimes {get;private set}
}

(看到人说,照着别人的代码敲一遍除了能看得仔细点没别的用处,那我就复制粘贴吧hhhh,其实这个是书上的例子,这里用来备忘,方便查看,而示例代码是用来试用来看不是用来“照着敲”的)

使用:

[DoesInterestingThings(1000, WhatDoesItDo = "voodoo")] // 如果不在特性中定义此公共属性,而是在类 DecoratedClass 中一样能找到吗?《高级编程》中的警告
public class DecoratedClass {}

访问:

Type classType = typeof(DecoratedClass);
object[] customAttributes = classType.GetCustomAttributes(true);
foreach(object customAttribute in customAttributes)
{
    DoesInterestingThingsAttribute interestingAttribute = customAttribute as DoesInterestingThingsAttribute;
    if (interestingAttribute != null)
    {
        print($"This class does {interestingAttribute.WhatDoesItDo} x "+$" {interestingAttribute.HowManyTimes}!");
    }
}

通过这个小栗子,可以从直观上理解如何创建以及使用特性。


Reflection,反射,是用来读取特性的值,可以在运行时动态检查类型信息。

反射可以获取保存在 Type 对象中的使用信息,以及通过 System.Reflection 名称空间中的类型来获取不同的类型信息。反射技术可以从 Type 对象中获取成员信息,基于此获取特性信息。当获取到不同特性时,代码中即可对不同特性信息采取不同的操作。


在《C# 高级编程》中有更进一步的讲解,我一直在思考怎么样读书才是最佳的方式,应该是看多的知识不能忘记吧,最起码,能找到书中对应每一个术语或知识点的位置。

在第16章中,主题是“反射、元数据和动态编程”,讨论了自定义特性、反射和动态编程,当然反射的功能非常庞大,书中也只给出了常用的功能介绍。

首先要介绍反射功能的入口 System.Type。

// typeof 运算符
Type t1 = typeof(double);
// 在一个变量上调用 GetType() 方法
double d = 10;
Type t2 = d.GetType();
// 静态方法
Type t3 = Type.GetType("System.Double");

简单来说,Type 有三类属性,字符串属性,引用属性和布尔属性,而 Type 的方法一般用于获取对应数据类型的成员信息(构造函数,属性,方法,事件等)。可以参考这里。

特性可以应用在程序元素上,而程序集 Assembly 也是其中之一,用在程序集上的特性可以在程序集的任意位置。Assembly 类定义在 System.Reflection 名称空间中,用来访问给定程序集的元数据(现在看基本等价于特性),同时可包含了加载和执行程序集的方法。

Assembly a1 = Assembly.Load("<AssemblyName>");
Assembly a2 = Assembly.LoadFrom(@"C:\<Path>\<AssemblyName>")

与 Type 类似,Assembly 类也可以获得相应程序集上定义的类型的详细信息,Assembly.GetTypes() 方法,返回一个 System.Type 的引用数组。

用于查找在程序集或类型中定义了哪些自定义特性的方法,取决于与该特性相关的对象类型。通过调用 Attribute 类的静态方法 GetCustomAttributes() 可以查找程序集从整体上关联了哪些自定义特性。

// 获取程序集整体上关联的自定义特性,获取程序集定义的所有自定义特性
Attribute[] definedAttributes = Attribute.GetCustomAttributes(assembly1);
// 获取感兴趣的特性类的 Type 对象的所有特性
Attribute[] supportsAttribute = Attribute.GetCustomAttributes(assembly1, typeof(SupportsWhatsNewAttribute));

在 C# 6.0 中,还没有这种用法,可以使用 Assembly 对象的实例方法获取。文档在这里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值