C#笔记(反射与特性)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38801354/article/details/79884391

1 反射

1.1 元数据和反射

有关程序集其类型的数据被称为 元数据,它们保存在程序的程序集中。
一个运行的程序查看本身的元数据或其他程序的元数据的行为叫做 反射

1.2 Type

每一个类型都有自己的成员特性,BCL声明了一个 Type 抽象类,它用来包含类型的特性,即使用 Type 类可以反射数据。
对于程序中用到的每个类型,CLR都会创建一个包含这个类型信息的 Type 类型的对象(在技术角度来讲是一个BCL内部的Type派生类型的对象),即每个类型都会关联到独立的 Type 类的对象,无论该类型创建了多少个实例,都只会有一个 Type 对象与之关联。
在一个 Type 对象中可以获取需要了解的有关类型几乎所有的信息,以下列出比较有用的 Type 类成员:

  • Name:属性,返回类型的名字
  • Namespace:属性,返回命名空间
  • Assembly:属性,返回声明类型的程序集
  • GetFields:方法,返回类型的字段列表
  • GetProperties:方法,返回类型的属性列表
  • GetMethods:方法,返回类型的方法列表

1.2.1 获取 Type 对象

在使用之前,要使用 System.Reflection 命名空间

使用 GetType 方法Typeof 运算符 可以获取 Type 对象。
对应实例,使用GetType 方法获取 Type 对象:

Type T = myInstance.GetType();

对应类型 使用 typeof 运算符获取 Type 对象:

Type t = typeof(DerivedClass);

2 特性

特性是一种允许我们向程序的程序集增加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的

  • 拥有特性的代码程序叫做 目标
  • 获取和使用元数据的代码程序叫做 特性的消费者

编译器获取源代码并从特性中产生元数据,然后把元数据放到程序集中;
消费者程序 可以获取特性的元数据以及程序中其他组件的元数据,即编译器同时生产和消费特性

特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集
个人感觉特性可以相对于预处理指令,区别在于预处理指令作用是指明编译器如何生成程序集,而特性则是作用在程序集中。

使用前置 特性片段 来应用特性,特性片段被方括号包围,方括号中为 特性名特性的参数列表

我们可以使用 assembly:module: 来设定全局特性,即程序集或块级别,程序集级别的特性一般都要放置在如何命名空间之外,通常放置在 AssemblyInfo.cs 文件中

2.1 预定义的保留特性

2.1.1 Obsolete 特性

在编写程序时,很多时候需要编写新方法来替代旧方法,这时可以使用 Obsolete 特性将程序结构标注为过期。
例如:

class Program
{
    [Obsolete("Use method SuperPrintOut")]
    static void PrintOut(string str)
    {
        Console.WriteLine(str);
    }
    static void Main(string[] args)
    {
        PrintOut("Start of Main");
    }
}

Obsolete 特性还有一个重载接受一个布尔值作为第二个参数,这个参数指定目标是否被标记为 错误 而不仅仅是 警告

2.1.2 Conditional 特性

Conditional 特性以编译符号作为参数来使用,如果编译符号已经定义了,那么编译器会调用该特性所包含的代码就和普通方法没有区别,否则编译器会忽略代码中这个方法的调用。
这与 #if 预处理指令相比,不同的是定义方法的CIL代码还是会在程序集中,只是在调用代码的时候会被忽略。

2.1.3 调用者信息特性

调用者信息特性可以访问很多关于调用者源代码的信息,如:

  • CallerFilePath:访问文件路径
  • CallerLineNumber:代码行数
  • CallerMemberName:调用成员的名称

注意这些特性只能用于方法中的可选参数,例如:

using System;
using System.Runtime.CompilerServices;

public static class Program
{
    public static void MyTrace(
                                string message,
                                [CallerFilePath] string fileName = '',
                                [CallerLineNumber] int lineNumber = 0,
                                [CallerMemberName] string MemberName = ''
                                )
    {
        Console.WriteLine("File: {0}", fileName); // 文件路径 + 文件名
        Console.WriteLine("lineNumber: {0}", lineNumber); // 19
        Console.WriteLine("Call from: {0}", MemberName); //Main
        Console.WriteLine("Msg: {0}", message); //Simple Message
    }
    public static void Main()
    {
        MyTrace("Simple Message");
    }
}

2.1.4 DebuggerStepThrough 特性

在单步调试代码时,常常希望调用器不要进入某些方法,而 DebuggeStepThrough 特性告诉调试器在执行目标代码时不要进入该方式调试。使用 DebuggeStepThrough 需要注意的是:

  • 该特性位于 System.Diagnostics 命名空间
  • 该特性可用于 类、结构、构造函数、方法或访问器

2.2 自定义特性

用户自定义的特性叫做自定义特性,所有特性类都派生自 System.Attribute.特性类应该表示目标结构的一些状态

2.2.1 声明自定义特性

声明一个特性类与声明一个普通类几乎一样,只是要注意:

  • 特性类派生自一个叫 System.Attribute
  • 给它命名的名称要以后缀 Attribute 结尾(而当目标应用特性时,可以选择省略后缀,省略后缀之后叫做 短名称
  • 通常为了安全,要声明类时要添加 sealed 关键字
  • 特性类的成员只能是:字段属性构造函数
  • 应用特性时,其特性若是含有带参数的构造函数的类,那么实参必须是在编译期就能确定是常量表达式

2.2.2 限制特性使用

可以为类添加特性,而特性本身又是类,所以可以有一个很重要的预定义特性用作于自定义特性上,而AttributeUsage 特性也就是一个预定义特性,我们可以使用它来限制特性使用在某个目标类型上。
AttributeUsage 特性作用于自定义特性类,其中它的公共属性有三个:

  • validOn:保存目标类型,可接受目标类型是 AttributeTarget 枚举的成员,可以使用 按位 或运算符来组合使用
  • Inherited:布尔值,默认值为 ture,表示特性是否能被继承
  • AllowMutiple:布尔值,默认值为 false,表示目标是否能被应用多个特性

例如:

[AttributeUsage(AttributeTarget.Method | AttributeTarget.Constructor, 
                Inherited = false, 
                AllowMutiple = false)]
public sealed class MyAttributeAttribute: System.Attribute
{
    ....
}

2.2.3 访问特性

使用 Type 实例的 IsDefinedGetCustomAttributes 方法可以访问特性

2.2.3.4 IsDefined 方法

使用 IsDefined 方法可以查询特性是否添加到某个类上,它接受两个参数:

  • 是否存在的特性的 Type 对象
  • 布尔值,是否搜索它的继承树来查找这个特性

eg:

Type t = mc.GetType();
bool isDefined = t.IsDefined(tyoeof(MyAttribute), false);

2.2.3.5 GetCustomAttributes 方法

使用 GetCustomAttributes 方法可以输出应用到的特性数组,接受一个布尔值参数,用于表示是否搜索它的继承树来查找特性。此时输出的都是 object 类型,所以必须使用类如 as 的转换符,强制转换相应的特性类型

eg:

object[] Attr = t.GetCustomAttributes(false); 

猜你喜欢

转载自blog.csdn.net/qq_38801354/article/details/79884391