(十二)反射与特性 -反射与预定义特性(1)

news/2024/10/28 21:30:52/

一、反射

1、什么是反射

  • 了解反射之前,要先了解一下元数据。元数据指保存在程序集中的一些有关程序及其类型的数据,包括类、结构、委托、接口和枚举等)的成员和成员的信息。

  • 程序在运行时,可以查看程序集以及其本身的元数据,是反射。

  • 通过反射,可以在运行时获取程序或程序集中的所有类型的元数据。

  • 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。(来源:菜鸟教程)

  • 反射的命名空间是System.Reflection。

具体内容:

.NET 中的反射

2、反射的优缺点

首先在编译中分为动态编译和静态编译,静态编译是在编译中确定类型,绑定对象,而动态编译是在运行中确定类型,绑定对象
反射的优点就是可以动态创建对象、绑定对象,提高了程序的灵活性和扩展性,但反射是一种解释操作,在性能上不如静态编译快

3、 反射(Reflection)的用途

反射(Reflection)有下列用途:

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

4、Type 类

通过反射类的Type,来获取有关构造函数、方法、字段、属性和事件的信息。

Type 是抽象类。在运行时,CLR 创建从 Type(RuntimeType)派生的类的实例,Type 包含了类型信息。当访问这些实例时,CLR 不会返回派生类的引用而是返回 Type 基类的引用

如声明了一个 MyClass 类型,不管创建了多少个 MyClass 类型的实例,就只有一个 Type 对象表示它。即每一个类型对应一个 Type 类型的对象。

表-System.Type 类的部分成员

成员成员类型描述
Name属性返回类型的名字
Namespace属性返回包含类型声明的命名空间
Assembly属性返回声明类型的程序集。如果类型是泛型的,返回定义这个类型的程序集
GetFields方法返回类型的字段列表
GetProperties方法返回类型的属性列表
GetMethods方法返回类型的方法列表

1)获取 Type 对象

通过 object 对象 来获取 Type 对象。

Type t = myInstance.GetType():

示例:

class BaseClass{public int BaseField = 0;}class DerivedClass: BaseClass{public int DerivedField = 0;}class Program{static void Main(string[] args){var bc = new BaseClass();var dc = new DerivedClass();BaseClass[] bca = new BaseClass[] { bc, dc };foreach(var v in bca){Type t = v.GetType();Console.WriteLine($"Object type :{ t.Name }");FieldInfo[] fi = t.GetFields();foreach (var f in fi)Console.WriteLine($"     Field :{ f.Name }");Console.WriteLine();}//Arry 枚举器的Type 类型int[] arr = { 10, 11, 12, 13 };var arrEnumerator = arr.GetEnumerator();Type ArrEnumeratorType = arrEnumerator.GetType();var arrEnumeratorProperties = ArrEnumeratorType.GetProperties();Console.WriteLine($"Object type :{ ArrEnumeratorType.Name }");foreach (var item in arrEnumeratorProperties)Console.WriteLine($"   Properties:{ item.Name }");Console.ReadKey();}}

输出结果:

Object type :BaseClassField :BaseFieldObject type :DerivedClassField :DerivedFieldField :BaseFieldObject type :SZArrayEnumeratorProperties:Current

2)使用 typeof 运算符来获取 Type 对象。

    class BaseClass{public int BaseField = 0;}class DerivedClass: BaseClass{public int DerivedField = 0;}class Program{static void Main(string[] args){Type tbc = typeof(DerivedClass);Console.WriteLine($"Object type :{ tbc.Name}");FieldInfo[] fi = tbc.GetFields();foreach (var f in fi)Console.WriteLine($"      Field : { f.Name }");Console.ReadKey();}}

输出结果:

Object type :DerivedClass
Field : DerivedField
Field : BaseField

二、特性

1、什么是特性

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

  • 将应用特性的程序结构叫作目标。
  • 设计用来获取和使用元数据的程序(比如对象浏览器)叫作特性的消费者。
  • .NET 预定了很多特性,我们也可以声明自定义特性。

关于图中的特性:

  • 在源码中将特性应用于程序结构。
  • 编译器获取源代码并且从特性产生元数据,然后把元数据放到程序集中。
  • 消费者程序可以获取特性的元数据以及程序中其他组件的元数据,注意,编译器同时产生和消费特性。

请添加图片描述

2、应用特性

特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集。

[Serializable]
public class MyClass
{...}[MyAttribute("Simple class","Version 3.57")]//带有参数的特性
public class MyOtherClass
{...}

3、预定义的保留特性

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 方法
}
}

在 VS 底部栏,错误列表里,显示警告信息:

严重性 代码 说明 项目 文件 行
警告 CS0618 “Program.PrintOut(string)”已过时:“Use method SuperPrintOut”…

如将 Obsolete 特性改为:

[Obsolete("Use method SuperPrintOut",true)]

在 VS 底部栏,错误列表里,显示错误信息:

严重性 代码 说明 项目 文件 行
错误 CS0619 “Program.PrintOut(string)”已过时:“Use method SuperPrintOut”…

2)Conditional 特性

Conditional 特性允许我们包括或排斥特定方法的所有调用。

在方法上使用 Conditional 特性的规则:

  • 该方法必须是类型或结构体的方法。
  • 该方法必须为 void 类型。
  • 该方法不能被声明 overrride,但可以标记为 virtual。
  • 该方法不能是接口方法的实现。
#define DoTrace
using System;
//....class Program{[Conditional("DoTrace")]static void TraceMessage(string str){Console.WriteLine(str);}static void Main(string[] args){TraceMessage("Start of Main");Console.WriteLine("Doing work in Main");TraceMessage("End of Main");Console.ReadKey();}}

若定义了编译符号#define DoTrace,输出结果:

Start of Main
Doing work in Main
End of Main

若没有定义编译符号#define DoTrace,输出结果:

Doing work in Main

3)调用者信息特性

利用调用者信息特性可以访问文件路径、代码行数、调用成员的名称等源代码信息。

  • 这3个特性名称为 CallerFilePath、CallerLineNumber 和 CallerMemberName。
  • 这些特性只能用于方法中的可选参数。
    class Program{public static void MyTrace(string message,[CallerFilePath] string fileName = "",[CallerLineNumber] int lineNumber = 0,[CallerMemberName] string callingMember=""){Console.WriteLine($"File:   { fileName }");Console.WriteLine($"Line:   { lineNumber }");Console.WriteLine($"Called From:   { callingMember }");Console.WriteLine($"Message:   { message }");}static void Main(string[] args){MyTrace("Simple message");Console.ReadKey();}}

输出结果:

File: C:\Users\用户名\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs
Line: 35
Called From: Main
Message: Simple message

4)DebuggerStepThrough 特性

在单步调试代码时,可以通过 DebuggerStepThrough 特性不让调试器进入某些方法里进行调试,而是执行该方法后,直接继续调试下一行代码。

DebuggerStepThrough 特性:

  • 该特性位于 System.Diagnostics 命名空间。
  • 该特性可用于类、结构、构造函数、方法或访问器。
    class Program{int x = 1;int X{get { return x; }[DebuggerStepThrough] //不进入 set 访问器set{x = x * 2;x += value;}}public int Y { get; set; }[DebuggerStepThrough]//不进入这个方法void IncrementFields(){X++;Y++;}static void Main(string[] args){Program p = new Program();p.IncrementFields();p.X = 5;Console.WriteLine($"X = { p.X }, Y = { p.Y }");Console.ReadKey();}}

调试过程:

请添加图片描述

4、其他预定义特性

特性意义
CLSCompliant声明公开暴露的成员应该被编译器检测其是否符合 CLS。兼容的程序集可以被任何兼容 .NET 的语言使用
Serializable声明结构可以被序列化
NonSerialized声明结构不能被序列化
DLLImport声明是非托管代码实现的
WebMethod声明方法应该被作为 XML Web 服务的一部分暴露
AttributeUsage声明特性能应用于什么类型的程序结构。将这个特性应用到特性声明上

5、多个特性

//多层结构
[Serializable]
[MyAtrribute("Simple class","Version 3.57")]//逗号分隔
[MyAtrribute("Simple class","Version 3.57"),Serializable]

6、其他类型的目标

目标:字段和属性等程序结构

//字段上的特性
[MyAtrribute("Holds a value","Version 3.2")]
public int MyField;//方法上的特性
[Obsolete]
[MyAtrribute("Prints out a message.","Version 3.6")]
public void PrintOut()
{
...
}

显示地标注特性:

[method: MyAtrribute("Prints out a message.","Version 3.6")]
[return: MyAtrribute("This value represents...","Version 2.3")]
public long ReturnSetting()
{
...
}

表-特性目标:
其中 type 覆盖了类、结构、委托、枚举和接口; typevar 目标名称为使用泛型的结构指定类型参数。

特性目标
eventfield
methodparam
propertyreturn
typetypevar
assemblymodule

7、全局特性

通过使用 assembly 和 module 目标名称来使用显式目标说明符把特性设置在程序集或模块级别。

  • 程序集级别的特性必须放置在任何命名空间之外,并且通常放置在 AssemblyInfo.cs 文件中。
  • AssemblyInfo.cs 文件通常包含有关公司、产品以及版权信息的元数据。

AssemblyInfo.cs 文件中代码段:

[assembly: AssemblyTitle("SuperWidget")]
[assembly: AssemblyDescription("Implements the SuperWidget product.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("McArthur Widgets,Inc.")]
[assembly: AssemblyProduct("Super Widget Deluxe")]
[assembly: AssemblyCopyright("Copyright McArthur Widgets 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

http://www.ppmy.cn/news/485938.html

相关文章

OpenHarmony端云一体化应用开发快速入门练习(中)登录认证

一、登录认证手机 可以在应用中集成手机帐号认证方式,您的用户可以使用“手机号码密码”或者“手机号码验证码”的方式来登录您的应用。 (一)前提条件 需要在AGC控制台开通认证服务。 需要先在您的应用中集成SDK。 (二&#xff…

2017年电气报价软件排名!评分最高的是它

↑『电老虎dianlaohu』关注中国专业的电气产品销售与服务平台!电气人必关注的微信平台:技术分享、学习交流、业务合作。 ▲电气报价类软件最常用的是:EP精灵和ExWinner成套报价软件。 ▲电气绘图类软件最常用的是:浩辰CAD2017…

电气产品报价单模板

模拟电气产品和多路转换器的作用主要用于信号的切换,目前集成模拟电子电气产品在小信号领域已成为主导产品,与以往的机械触点式电子电气产品相比,集成电子开关有许多优点,例如,切换速率快、无抖动、耗电省、体积小、工作可靠且容易控制等。接下来那我们来详细了解一下电气产品报…

电气论文:基于价格弹性矩阵的需求侧响应(python实现)

专栏解锁后可以查看该专栏所有文章 文章目录 讲解代码讲解 参考文章 代码 首先看下电价是如何影响负荷的 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # @Author: yudengwu(余登武)

全球与中国电气用有机硅弹性体市场深度研究分析报告

【报告篇幅】:98 【报告图表数】:140 【报告出版时间】:2022年1月 报告摘要 2021年全球电气用有机硅弹性体市场销售额达到了 亿美元,预计2028年将达到 亿美元,年复合增长率(CAGR)为 %&#…

电气simulink常用模块_干货丨16种常用模块电路分析,工程师的必备~

电路图一大张,看似复杂,但也都是由一小块一小块的功能模块组成的。因此要根据大的功能先划分成块,再在块里面看是通过什么电路形式实现的,有些起辅助作用,有些起主要作用。下面小编给大家整理了16种常用的模块电路分析,希望对大家有帮助。 1. RS232通讯电路 双路232通信电…

全球与中国高压电气机柜市场现状及未来发展趋势(2022)

根据QYR(恒州博智)的统计及预测,2021年全球高压电气机柜市场销售额达到了 亿美元,预计2028年将达到 亿美元,年复合增长率(CAGR)为 %(2022-2028)。地区层面来看&#xff0…

医院PACS系统的发展历史

PACS全称Picture Archivingand Communication Systems。它是应用在医院影像科室的系统,主要的任务就是把日常产生的各种医学影像(包括核磁,CT,超声,X光机,红外仪、显微仪等设备产生的图像)通过各…