C# 预处理器指令,特性,反射
预处理器指令
预处理器指令(Preprocessor Directives)是编程语言中的一类指令,用于在编译之前对代码进行预处理。在C#中,预处理器指令主要用于控制编译过程,例如条件编译、宏定义等。
C# 预处理器指令列表
下表列出了 C# 中可用的预处理器指令:
指令 | 描述 |
---|---|
#define | 定义一个符号,可以用于条件编译。 |
#undef | 取消定义一个符号。 |
#if | 开始一个条件编译块,如果符号被定义则包含代码块。 |
#elif | 如果前面的 #if 或 #elif 条件不满足,且当前条件满足,则包含代码块。 |
#else | 如果前面的 #if 或 #elif 条件不满足,则包含代码块。 |
#endif | 结束一个条件编译块。 |
#warning | 生成编译器警告信息。 |
#error | 生成编译器错误信息。 |
#region | 标记一段代码区域,可以在IDE中折叠和展开这段代码,便于代码的组织和阅读。 |
#endregion | 结束一个代码区域。 |
#line | 更改编译器输出中的行号和文件名,可以用于调试或生成工具的代码。 |
#pragma | 用于给编译器发送特殊指令,例如禁用或恢复特定的警告。 |
#nullable | 控制可空性上下文和注释,允许启用或禁用对可空引用类型的编译器检查。 |
预处理指令使用
#define
和 #undef
#define
:用于定义符号。#undef
:用于取消定义符号。
#if
、#elif
、#else
、#endif
#if
:根据条件编译代码块。#elif
:#else if
的缩写,与#if
联合使用。#else
:定义#if
或#elif
条件不满足时的代码块。#endif
:结束条件编译块。
1 | // 注意,在使用Visual Studio进行调试时,Debug是默认被定义的 |
#warning
和 #error
- **
#warning
**:生成编译警告。 - **
#error
**:生成编译错误,阻止编译过程。
1 |
#region
和 #endregion
#region
和#endregion
用于代码折叠和组织,可以在IDE中折叠和展开代码块,提升代码的可读性。
1 |
|
#pragma
#pragma
指令用于修改编译器的行为,常见的有 **#pragma warning
**,用于禁用或启用特定的警告。
1 |
|
#line
#line
指令可以更改编译器报告的文件名和行号,常用于代码生成工具,以确保错误信息对应于生成的源文件。
1 |
|
特性
特性(Attribute)是一种用于在元数据中添加信息的声明性标签。特性可以附加到代码的各个部分,如类、方法、属性、字段、程序集等,以提供关于代码行为的额外信息。特性不会直接影响代码的运行,但可以被工具、编译器或运行时用来修改行为或执行特定任务。
特性是一种继承自System.Attribute
类的类,表示在程序集中添加的声明性信息。
使用特性
特性通常放在声明前一行,用方括号包围[]
1 | [// 标记为可被序列化 ] |
常用内置特性
C# 中提供了许多内置特性,常见的包括:
[Serializable]
: 指定类可以被序列化。[Obsolete]
: 标记已过时的程序元素,并可选择显示警告或错误信息。[DllImport]
: 用于导入非托管的 DLL 函数,通常用于调用 Win32 API。[Conditional]
: 指示编译器仅在指定符号定义时才调用某个方法。[AttributeUsage]
: 定义特性可以应用到哪些程序元素,以及它们是否可以多次应用。
这些都是缩写,省略了名称后的Attribute
,如[ObsoleteAttribute]
示例
1 |
|
执行结果
1 | 过时的Method1 |
WinForm常用内置特性
这些内置特性一般配合用户自定义控件使用
[Description]
:提供属性或事件的简短描述,该描述会在属性窗口中显示为提示。[Category]
:指定属性在属性窗口中所属的分类。[Browsable]
:控制属性是否在属性窗口(Property Grid)中可见。[DefaultValue]
:指定属性的默认值。设计器使用此值来决定是否需要生成代码来设置属性的值。[Editor]
:指定属性在属性窗口中使用的编辑器。通常用于需要自定义编辑器的复杂属性。1
2[ ]
public Font CustomFont { get; set;}
自定义特性
可以创建自定义特性,通过继承 System.Attribute
类来实现。自定义特性可以包含构造函数、属性和方法。自定义特性的名称一般以Attribute
作为结尾
1 | // 通过AttributeUsage,限定该特性只能用在类和方法上,Inherited表示该特性能否被派生类继承,默认false |
读取特性
特性的读取需要用到反射(Reflection),下面的示例是一个从自定义特性到读取特性的完整示例
1 | namespace ConsoleApp1 |
反射
反射(Reflection)是C#中的一种强大功能,它允许程序在运行时动态地检查和操作对象的类型、属性、方法和其他元数据。通过反射,程序可以获取关于类和对象的信息,并执行与类型相关的操作,而无需在编译时了解这些类型的确切名称或结构。
优缺点
优点:
- 1、反射提高了程序的灵活性和扩展性。
- 2、降低耦合性,提高自适应能力。
- 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
缺点:
- 1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
- 2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。
为了提高性能,可以:
- 缓存反射结果: 将
Type
、MethodInfo
、PropertyInfo
等对象缓存起来,避免重复获取。 - 尽量减少反射调用次数: 在设计时尽量减少使用反射的需求,或将反射用于初始化阶段,然后使用直接调用。
反射的核心类
反射功能主要由 System.Reflection
命名空间中的类提供。以下是一些常用的反射类:
Assembly
: 表示一个程序集,提供加载程序集和检索程序集信息的方法。Type
: 表示类型(类、接口、结构体等),提供获取类型信息的方法,如获取属性、方法、字段等。MethodInfo
: 提供有关方法的信息,并允许调用方法。PropertyInfo
: 提供有关属性的信息,并允许获取或设置属性的值。FieldInfo
: 提供有关字段的信息,并允许获取或设置字段的值。ConstructorInfo
: 提供有关构造函数的信息,并允许调用构造函数创建实例。
反射的基本操作
加载程序集
1
Assembly assembly = Assembly.Load("AssemblyName");
获取类型信息
1
2
3
4
5
6
7// 通过程序集获取
Type type1 = assembly.GetType();
// 使用typeof关键字获取
Type type2 = typeof(Rectangle);
// 通过Object.GetType()获取
Rectangle r = new Rectangle(2, 3);
Type type3 = r.GetType();获取属性,方法,字段等信息
1
2
3
4
5
6
7PropertyInfo? propertyInfo = type1.GetProperty("propertyName");
FieldInfo? fieldInfo = type1.GetField("fieldName");
MethodInfo? methodInfo = type1.GetMethod("methodName");
PropertyInfo[]? propertyInfos = type1.GetProperties();
FieldInfo[]? fieldInfos = type1.GetFields();
MethodInfo[]? methodInfos = type1.GetMethods();创建实例
1
2
3
4
5
6
7
8
9// 使用无参构造函数
object? instance1 = Activator.CreateInstance(type1);
// 使用有参构造函数
ConstructorInfo? constructorInfo = type1.GetConstructor(new Type[] { typeof(string), typeof(int) });
object? instance2 = constructorInfo?.Invoke(new object[] { "parameter1", 42 });
// 通过程序集创建实例
Assembly.Load("AssemblyName").CreateInstance("typeName");调用方法
1
2MethodInfo? methodInfo1 = type1?.GetMethod("methodName");
methodInfo?.Invoke(/* 要调用方法的对象*/new object(), new object[] { /* 参数列表 */ });获取和设置属性值
1
2
3
4
5PropertyInfo? propertyInfo1 = type1?.GetProperty("propertyName");
// 获取属性值
object? propertyValue = propertyInfo1?.GetValue(instance1);
// 设置属性值
propertyInfo1?.SetValue(instance1, "new value");获取和设置字段值
1
2
3
4
5FieldInfo? fieldInfo1 = type1?.GetField("fieldName");
// 获取字段值
object? fieldValue = fieldInfo1?.GetValue(instance1);
// 设置字段值
fieldInfo1?.SetValue(instance1, "new value");获取静态成员
1
2
3
4
5FieldInfo? staticFieldInfo = type1?.GetField("saticFieldName",BindingFlags.Static|BindingFlags.Public);
PropertyInfo? staticPropertyInfo = type1?.GetProperty("saticPropName",BindingFlags.Static|BindingFlags.Public);
MethodInfo? staticMethodInfo = type1?.GetMethod("staticMethodName",BindingFlags.Static | BindingFlags.Public);在使用反射获取静态成员时,通常需要指定
BindingFlags.Static
以表明要获取静态成员。此外,还可以使用其他BindingFlags
来精确控制反射行为,例如:- **
BindingFlags.Public
**:获取公共成员。 - **
BindingFlags.NonPublic
**:获取非公共(私有或保护)成员。 - **
BindingFlags.FlattenHierarchy
**:在获取静态成员时,包含继承自基类的公共和受保护的静态成员。
- **
获取元数据,见读取特性
附录
元数据
元数据(Metadata)是关于数据的数据,或描述数据的数据。在编程和计算机科学中,元数据提供了有关其他数据的信息或描述。它不直接影响数据的内容或行为,但为系统、工具或人类理解、管理和使用这些数据提供了上下文和背景。
元数据的例子
- 文件系统中的元数据: 在文件系统中,文件的元数据包括文件名、大小、创建日期、修改日期、文件类型、权限等。这些信息帮助操作系统和用户管理和组织文件,但并不是文件的实际内容。
- 数据库中的元数据: 数据库的元数据包括表的结构(如列的名称和数据类型)、索引、关系约束、存储过程定义等。元数据使数据库管理系统能够理解如何存储和检索数据。
- HTML 元数据: 在网页中,HTML 的
<meta>
标签用于指定页面的描述、关键字、作者、以及字符集等信息。搜索引擎和浏览器使用这些信息来优化搜索结果和显示页面内容。 - 编程中的元数据: 在编程中,元数据可以是代码中的注释、特性(如前面提到的 C# 特性)、文档字符串等。这些元数据用于提供有关代码结构、行为或目的的附加信息。
C# 中的元数据
在 C# 中,特性(Attributes)就是元数据的一种形式。它们为代码的某些部分(如类、方法、属性等)提供附加的信息。例如:
1 | [ ] |
这里的 [Obsolete]
特性是元数据,提供了关于 OldMethod
的附加信息,告知编译器和开发者这个方法已经过时。
元数据的作用
- 描述数据: 提供对数据的描述或解释,使用户或系统了解数据的含义和使用方法。
- 数据管理: 帮助管理和组织数据,如通过描述数据的结构、格式和访问权限来管理文件、数据库或其他数据存储。
- 数据发现和检索: 通过提供关键词或描述,元数据可以帮助用户或系统更快地查找和检索数据。
- 编译器优化和代码分析: 编译器可以利用元数据优化代码或生成警告和错误消息。
- 运行时行为: 在某些情况下,元数据会影响代码的运行时行为。例如,序列化库可以通过读取元数据来决定如何序列化对象。
参考内容
参考内容:
- 标题: C# 预处理器指令,特性,反射
- 作者: 日之朝矣
- 创建于 : 2024-08-18 17:08:07
- 更新于 : 2024-08-18 09:25:27
- 链接: https://blog.rzzy.fun/2024/08/18/csharp-preprocessor-directives-attributes-reflection/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。