C# 反射与特性:深入探索运行时类型系统与元数据编程

article/2025/6/21 4:35:32

在C#开发中,我们通常编写静态类型的代码——编译器在编译时就知道所有类型信息。然而,.NET框架提供了一套强大的机制,允许我们在运行时检查、发现和使用类型信息,这就是反射(Reflection)。而与反射密切相关的另一项技术是特性(Attribute),它为我们提供了一种向代码添加元数据的声明式方法。本文将全面探讨这两项技术,从基础概念到高级应用场景。

第一部分:反射基础

1.1 什么是反射?

反射是.NET框架的核心功能之一,它允许程序在运行时:

  • 检查类型信息(类、接口、结构等)

  • 动态创建对象实例

  • 调用方法和访问属性/字段

  • 修改程序行为而不需要重新编译

反射的核心是System.Type类,它代表了类型声明。每个加载到应用程序域中的类型都有一个相关的Type对象。

1.2 获取Type对象的三种方式

// 1. 使用typeof运算符
Type t1 = typeof(string);// 2. 使用对象的GetType()方法
string s = "hello";
Type t2 = s.GetType();// 3. 使用Type.GetType()静态方法
Type t3 = Type.GetType("System.String");

1.3 反射的基本操作

检查类型信息

Type type = typeof(DateTime);Console.WriteLine($"类型名: {type.Name}");
Console.WriteLine($"全名: {type.FullName}");
Console.WriteLine($"命名空间: {type.Namespace}");
Console.WriteLine($"是类吗? {type.IsClass}");
Console.WriteLine($"是值类型吗? {type.IsValueType}");
Console.WriteLine($"基类型: {type.BaseType}");

检查成员信息

// 获取所有公共方法
MethodInfo[] methods = type.GetMethods();
foreach (MethodInfo method in methods)
{Console.WriteLine($"方法: {method.Name}");Console.WriteLine($"  返回类型: {method.ReturnType}");ParameterInfo[] parameters = method.GetParameters();foreach (ParameterInfo param in parameters){Console.WriteLine($"  参数: {param.Name} ({param.ParameterType})");}
}// 获取属性
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo prop in properties)
{Console.WriteLine($"属性: {prop.Name} ({prop.PropertyType})");
}

动态创建实例

// 使用无参构造函数
object stringInstance = Activator.CreateInstance(typeof(string));// 使用带参构造函数
char[] chars = {'H','e','l','l','o'};
object strWithChars = Activator.CreateInstance(typeof(string), chars);
Console.WriteLine(strWithChars); // 输出 "Hello"

动态调用方法

Type mathType = typeof(Math);
MethodInfo maxMethod = mathType.GetMethod("Max", new Type[] { typeof(int), typeof(int) });int result = (int)maxMethod.Invoke(null, new object[] { 5, 10 });
Console.WriteLine($"Max(5, 10) = {result}"); // 输出 10

第二部分:特性详解

2.1 什么是特性?

特性是向程序集、类型、成员等代码元素添加声明性信息的机制。它们不会直接影响代码的执行,但可以通过反射在运行时被读取和使用。

2.2 内置常用特性

.NET框架提供了许多有用的内置特性:

[Serializable] // 标记类可序列化
public class Person
{[Obsolete("该方法已过时,请使用NewMethod代替", true)] // 标记方法过时public void OldMethod() { }public void NewMethod() { }[NonSerialized] // 标记字段不序列化private string secret;[DllImport("user32.dll")] // 声明外部DLL方法public static extern int MessageBox(int hWnd, string text, string caption, int type);
}

2.3 自定义特性

创建自定义特性需要继承自System.Attribute类:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class AuthorAttribute : Attribute
{public string Name { get; }public double Version { get; set; }public AuthorAttribute(string name){Name = name;}
}
  • AttributeUsage特性用于指定自定义特性的使用规则:

    • AttributeTargets - 指定特性可以应用的目标

    • AllowMultiple - 是否允许多次应用于同一目标

    • Inherited - 是否可被派生类继承

2.4 使用自定义特性

[Author("John Doe", Version = 1.0)]
[Author("Jane Smith", Version = 1.1)]
public class Document
{[Author("John Doe")]public void Save() { }
}

2.5 通过反射读取特性

Type docType = typeof(Document);// 获取类上的特性
object[] attrs = docType.GetCustomAttributes(typeof(AuthorAttribute), false);
foreach (AuthorAttribute attr in attrs)
{Console.WriteLine($"作者: {attr.Name}, 版本: {attr.Version}");
}// 获取方法上的特性
MethodInfo saveMethod = docType.GetMethod("Save");
attrs = saveMethod.GetCustomAttributes(typeof(AuthorAttribute), false);
foreach (AuthorAttribute attr in attrs)
{Console.WriteLine($"方法作者: {attr.Name}");
}

第三部分:高级应用场景

3.1 插件系统实现

反射是实现插件架构的理想选择:

public interface IPlugin
{string Name { get; }string Description { get; }void Execute();
}public class PluginLoader
{public IEnumerable<IPlugin> LoadPlugins(string pluginsDirectory){if (!Directory.Exists(pluginsDirectory))throw new DirectoryNotFoundException(pluginsDirectory);var plugins = new List<IPlugin>();foreach (string dllPath in Directory.GetFiles(pluginsDirectory, "*.dll")){try{Assembly assembly = Assembly.LoadFrom(dllPath);foreach (Type type in assembly.GetTypes()){if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface){IPlugin plugin = (IPlugin)Activator.CreateInstance(type);plugins.Add(plugin);}}}catch (Exception ex){// 处理加载错误Console.WriteLine($"加载插件 {dllPath} 失败: {ex.Message}");}}return plugins;}
}

3.2 ORM框架中的特性映射

特性常用于ORM框架中实现对象-关系映射:

[AttributeUsage(AttributeTargets.Class)]
public class TableAttribute : Attribute
{public string Name { get; }public TableAttribute(string name){Name = name;}
}[AttributeUsage(AttributeTargets.Property)]
public class ColumnAttribute : Attribute
{public string Name { get; }public bool IsPrimaryKey { get; set; }public bool IsNullable { get; set; } = true;public ColumnAttribute(string name){Name = name;}
}[Table("Users")]
public class User
{[Column("user_id", IsPrimaryKey = true)]public int Id { get; set; }[Column("user_name")]public string Name { get; set; }[Column("email")]public string Email { get; set; }[Column("created_at", IsNullable = false)]public DateTime CreatedAt { get; set; }
}public class SqlGenerator
{public string GenerateCreateTableSql(Type entityType){var tableAttr = entityType.GetCustomAttribute<TableAttribute>();string tableName = tableAttr?.Name ?? entityType.Name;var columns = new List<string>();var primaryKeys = new List<string>();foreach (var prop in entityType.GetProperties()){var columnAttr = prop.GetCustomAttribute<ColumnAttribute>();if (columnAttr == null) continue;string columnName = columnAttr.Name;string columnType = GetSqlType(prop.PropertyType);string nullable = columnAttr.IsNullable ? "NULL" : "NOT NULL";columns.Add($"{columnName} {columnType} {nullable}");if (columnAttr.IsPrimaryKey)primaryKeys.Add(columnName);}string sql = $"CREATE TABLE {tableName} (\n  {string.Join(",\n  ", columns)}";if (primaryKeys.Count > 0)sql += $",\n  PRIMARY KEY ({string.Join(", ", primaryKeys)})";sql += "\n);";return sql;}private string GetSqlType(Type type){if (type == typeof(int)) return "INT";if (type == typeof(string)) return "VARCHAR(255)";if (type == typeof(DateTime)) return "DATETIME";if (type == typeof(bool)) return "BIT";// 添加更多类型映射...return "VARCHAR(255)";}
}

3.3 依赖注入容器

反射是实现依赖注入容器的核心技术:

public class DIContainer
{private readonly Dictionary<Type, Type> _mappings = new Dictionary<Type, Type>();public void Register<TInterface, TImplementation>() where TImplementation : TInterface{_mappings[typeof(TInterface)] = typeof(TImplementation);}public T Resolve<T>(){return (T)Resolve(typeof(T));}private object Resolve(Type type){Type implType;if (_mappings.TryGetValue(type, out implType)){// 获取第一个构造函数ConstructorInfo ctor = implType.GetConstructors()[0];// 获取构造函数参数ParameterInfo[] paramsInfo = ctor.GetParameters();// 解析所有参数object[] parameters = paramsInfo.Select(p => Resolve(p.ParameterType)).ToArray();// 创建实例return ctor.Invoke(parameters);}throw new InvalidOperationException($"未注册类型 {type.FullName}");}
}// 使用示例
var container = new DIContainer();
container.Register<ILogger, FileLogger>();
container.Register<IDatabase, SqlDatabase>();
container.Register<App, App>();App app = container.Resolve<App>();

第四部分:性能优化与最佳实践

4.1 反射的性能问题

反射操作比直接代码调用要慢得多,主要原因包括:

  • 运行时类型检查

  • 方法调用的间接性

  • 缺少编译时优化

4.2 优化反射性能的策略

缓存反射结果

public class ReflectionCache
{private static readonly Dictionary<Type, PropertyInfo[]> _propertyCache = new Dictionary<Type, PropertyInfo[]>();public static PropertyInfo[] GetProperties(Type type){if (!_propertyCache.TryGetValue(type, out var properties)){properties = type.GetProperties();_propertyCache[type] = properties;}return properties;}
}

使用Delegate.CreateDelegate

MethodInfo method = typeof(string).GetMethod("Substring", new Type[] { typeof(int) });
var substringDelegate = (Func<string, int, string>)Delegate.CreateDelegate(typeof(Func<string, int, string>), method);// 现在可以高效调用
string result = substringDelegate("Hello World", 6);

使用表达式树

MethodInfo method = typeof(string).GetMethod("Substring", new Type[] { typeof(int) });var param = Expression.Parameter(typeof(string), "s");
var arg = Expression.Parameter(typeof(int), "start");
var call = Expression.Call(param, method, arg);
var lambda = Expression.Lambda<Func<string, int, string>>(call, param, arg);Func<string, int, string> compiled = lambda.Compile();
string result = compiled("Hello World", 6);

4.3 最佳实践

  1. 避免过度使用反射:只在必要时使用反射,如插件系统、序列化等场景

  2. 封装反射代码:将反射代码封装在专门的类中,与业务逻辑分离

  3. 安全考虑:反射可以绕过访问修饰符限制,需注意安全性

  4. 异常处理:反射操作可能抛出多种异常,需妥善处理

  5. 文档记录:使用反射的代码应充分注释,说明其用途和限制

结语

反射和特性是C#强大的元编程工具,它们为框架开发、系统架构提供了极大的灵活性。虽然反射会带来一定的性能开销,但在合理的场景下使用,并配合适当的优化策略,可以构建出既灵活又高效的应用程序。理解这些技术的原理和适用场景,将使你能够更好地设计和实现复杂的系统架构。

 


http://www.hkcw.cn/article/cumFcYJLYp.shtml

相关文章

腾讯面试手撕题:返回行递增有序矩阵第k小的元素

题目 给定一个n行n列的矩阵&#xff0c;这个矩阵的每一行是递增有序的&#xff0c;求这个矩阵中第k小的元素。 解答 优解 基于二分查找和按行统计小于等于目标值的元素个数。算法的时间复杂度为&#xff0c;其中D是矩阵中元素值域的范围&#xff08;即最大值与最小值的差&a…

【PostgreSQL 02】PostgreSQL数据类型革命:JSON、数组与地理信息让你的应用飞起来

PostgreSQL数据类型革命&#xff1a;JSON、数组与地理信息让你的应用飞起来 关键词 PostgreSQL高级数据类型, JSONB, 数组类型, PostGIS, 地理信息系统, NoSQL, 文档数据库, 空间数据, 数据库设计, PostgreSQL扩展 摘要 PostgreSQL的高级数据类型是其区别于传统关系数据库的核心…

[Windows] 剪映 视频编辑处理

附链接&#xff1a;夸克网盘分享&#xff08;点击蓝色字体自行保存下载&#xff09;

NW994NX734美光固态闪存NX737NX740

NW994NX734美光固态闪存NX737NX740 在数字化浪潮汹涌澎湃的今天&#xff0c;数据存储技术如同一座坚实的基石&#xff0c;支撑着科技世界的大厦。美光固态闪存以其卓越的性能和创新的技术&#xff0c;在存储领域占据着重要的地位。本文将深入剖析NW994、NX734、NX737以及NX740…

C# 类和继承(使用基类的引用)

使用基类的引用 派生类的实例由基类的实例和派生类新增的成员组成。派生类的引用指向整个类对象&#xff0c;包括 基类部分。 如果有一个派生类对象的引用&#xff0c;就可以获取该对象基类部分的引用&#xff08;使用类型转换运算符把 该引用转换为基类类型&#xff09;。类…

VMvare 创建虚拟机 安装CentOS7,配置静态IP地址

创建虚拟机 安装CentOS7 设置网络模式 设置静态ip vim /etc/sysconfig/network-scripts/ifcfg-ens33 systemctl restart network

python:PyMOL 能处理 *.pdb 文件吗?

PyMOL 完全可以打开并处理 PDB&#xff08;Protein Data Bank&#xff09;文件&#xff0c;这是 PyMOL 最主要的功能之一。PDB 格式是结构生物学领域的标准文件格式&#xff0c;专门用于存储生物大分子&#xff08;如蛋白质、核酸&#xff09;的三维结构数据。 在 PyMOL 中打开…

【数据治理】要点整理-信息技术数据质量评价指标-GB/T36344-2018

导读&#xff1a;指标为数据质量评估提供了一套系统化、标准化的框架&#xff0c;涵盖规范性、完整性、准确性、一致性、时效性、可访问性六大核心指标&#xff0c;助力组织提升数据处理效率、支持决策制定及业务流程优化&#xff0c;确保数据在数据生存周期各阶段的质量可控。…

【Redis】hash 类型

hash 一. hash 类型介绍二. hash 命令hset、hgethexists、hdelhkeys、hvals、hgetallhmset、hmgethlen、hstrlen、hsetnxhincrby、hincrbyfloat 三. hash 命令小结四. hash 内部编码方式五. hash 的应用场景缓存功能缓存方式对比 一. hash 类型介绍 哈希表在日常开发中&#x…

ubuntu/windows系统下如何让.desktop/.exe文件 在开机的时候自动运行

目录 1&#xff0c;​​让 .desktop 文件在 Ubuntu 开机时自动启动​ 1.1 创建 autostart 目录&#xff08;如果不存在&#xff09;​ ​ 1.2 将 .desktop 文件复制到 autostart 目录​ ​ 1.3 确保 .desktop 文件有可执行权限​ 2,windows 2.1 打开「启动」文件夹​…

1-Wire 一线式总线:从原理到实战,玩转 DS18B20 温度采集

引言 在嵌入式系统中&#xff0c;通信总线是连接 CPU 与外设的桥梁。从 I2C、SPI 到 UART&#xff0c;每种总线都有其独特的应用场景。而本文要介绍的1-Wire 一线式总线&#xff0c;以其极简的硬件设计和独特的通信协议&#xff0c;在温度采集、身份识别等领域大放异彩。本文将…

有机黑鸡蛋与普通鸡蛋:差异剖析与选购指南

在我们的日常饮食结构里&#xff0c;鸡蛋始终占据着不可或缺的位置&#xff0c;是人们获取营养的重要来源。如今&#xff0c;市场上鸡蛋种类丰富&#xff0c;除了常见的普通鸡蛋&#xff0c;有机黑鸡蛋也逐渐崭露头角&#xff0c;其价格通常略高于普通鸡蛋。这两者究竟存在哪些…

Fastapi 学习使用

Fastapi 学习使用 Fastapi 可以用来快速搭建 Web 应用来进行接口的搭建。 参考文章&#xff1a;https://blog.csdn.net/liudadaxuexi/article/details/141062582 参考文章&#xff1a;https://blog.csdn.net/jcgeneral/article/details/146505880 参考文章&#xff1a;http…

数字化转型进阶:精读41页华为数字化转型实践【附全文阅读】

该文档聚焦华为数字化转型实践&#xff0c;核心内容如下&#xff1a; 转型本质与目标&#xff1a;数字化转型是通过数字技术穿透业务&#xff0c;实现物理世界与数字世界的融合&#xff0c;目标是支撑主业成功、提升体验与效率、探索模式创新。华为以 “平台 服务” 为核心&am…

共享内存-systemV

01. 共享内存简述 共享内存是一个允许多个进程直接访问同一块物理内存区域的进程通信工具&#xff0c;因其本身不涉及用户态与核心态之间转换&#xff0c;故效率最佳。为了使用一个共享内存段&#xff0c;一般需要以下几个步骤&#xff1a; 调用shmget()创建一个新共享内存段…

大语言模型值ollama使用(1)

ollama为本地调用大语言模型提供了便捷的方式。下面列举如何在windows系统中快捷调用ollama。 winR打开运行框&#xff0c;输入cmd 1、输入ollama list 显示已下载模型 2、输入ollama pull llama3 下载llama3模型 3、 输入 ollama run llama3 运行模型 4、其他 ollama li…

【基础算法】高精度(加、减、乘、除)

文章目录 什么是高精度1. 高精度加法解题思路代码实现 2. 高精度减法解题思路代码实现 3. 高精度乘法解题思路代码实现 4. 高精度除法 (高精度 / 低精度)解题思路代码实现 什么是高精度 我们平时使用加减乘除的时候都是直接使用 - * / 这些符号&#xff0c;前提是进行运算的数…

uni-data-picker级联选择器、fastadmin后端api

记录一个部门及部门人员选择的功能&#xff0c;效果如下&#xff1a; 组件用到了uni-ui的级联选择uni-data-picker 开发文档&#xff1a;uni-app官网 组件要求的数据格式如下&#xff1a; 后端使用的是fastadmin&#xff0c;需要用到fastadmin自带的tree类生成部门树 &#x…

MonitorSDK_性能监控(从Web Vital性能指标、PerformanceObserver API和具体代码实现)

性能监控 性能指标 在实现性能监控前&#xff0c;先了解Web Vitals涉及的常见的性能指标 Web Vitals 是由 Google 推出的网页用户体验衡量指标体系&#xff0c;旨在帮助开发者量化和优化网页在实际用户终端上的性能体验。Web Vitals 强调“以用户为中心”的度量&#xff0c;而…

Kubernetes架构与核心概念深度解析:Pod、Service与RBAC的奥秘

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言&#xff1a;云原生时代的操作系统 在云原生技术浪潮中&#xff0c;Kubernetes&#xff08;简称K8s&#xff09;已成为容器编排领域的"分布式操…