C#通过反射实现动态属性访问器

使用反射,我们可以创建一个动态的属性访问器(Dynamic Property Accessor),允许我们在运行时访问和修改对象的属性

为什么要动态访问

为什么不直接访问,而用动态访问?

  • 直接访问适用于:
    • 编译时就知道要访问的属性
    • 追求最高性能的场景
    • 简单的属性访问
  • 动态访问适用于:
    • 运行时才确定要访问的属性
    • 需要通用处理逻辑的场景
    • 框架开发
    • 配置驱动的数据处理
    • 动态数据映射和转换

动态访问虽然性能略低,但提供了更大的灵活性和通用性,特别适合开发框架和通用工具

完整样例

using System.Reflection;
using System.Text;

public class DynamicPropertyAccessor<T> where T : class
{
    private readonly Dictionary<string, PropertyInfo> _properties;

    public DynamicPropertyAccessor()
    {
        _properties = typeof(T).GetProperties()
            .ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
    }

    public bool HasProperty(string propertyName)
    {
        return _properties.ContainsKey(propertyName);
    }

    public IEnumerable<string> GetPropertyNames()
    {
        return _properties.Keys;
    }

    public Type GetPropertyType(string propertyName)
    {
        if (_properties.TryGetValue(propertyName, out PropertyInfo propInfo))
        {
            return propInfo.PropertyType;
        }
        throw new ArgumentException($"Property '{propertyName}' not found in type {typeof(T).Name}.");
    }

    public bool IsPropertyWritable(string propertyName)
    {
        if (_properties.TryGetValue(propertyName, out PropertyInfo propInfo))
        {
            return propInfo.CanWrite;
        }
        throw new ArgumentException($"Property '{propertyName}' not found in type {typeof(T).Name}.");
    }

    public object GetPropertyValue(T obj, string propertyName)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));

        if (string.IsNullOrEmpty(propertyName))
            throw new ArgumentNullException(nameof(propertyName));

        if (_properties.TryGetValue(propertyName, out PropertyInfo propInfo))
        {
            try
            {
                return propInfo.GetValue(obj);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"Error getting value for property {propertyName}", ex);
            }
        }
        throw new ArgumentException($"Property '{propertyName}' not found in type {typeof(T).Name}.");
    }

    public void SetPropertyValue(T obj, string propertyName, object value)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));

        if (string.IsNullOrEmpty(propertyName))
            throw new ArgumentNullException(nameof(propertyName));

        if (_properties.TryGetValue(propertyName, out PropertyInfo propInfo))
        {
            try
            {
                if (!propInfo.CanWrite)
                {
                    throw new InvalidOperationException($"Property {propertyName} is read-only.");
                }

                if (value != null && !propInfo.PropertyType.IsAssignableFrom(value.GetType()))
                {
                    value = Convert.ChangeType(value, propInfo.PropertyType);
                }

                propInfo.SetValue(obj, value);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException(
                    $"Error setting value for property {propertyName}. Expected type: {propInfo.PropertyType.Name}", ex);
            }
        }
        else
        {
            throw new ArgumentException($"Property '{propertyName}' not found in type {typeof(T).Name}.");
        }
    }

    public void BatchUpdateProperties(T entity, Dictionary<string, object> updates)
    {
        if (entity == null)
            throw new ArgumentNullException(nameof(entity));
        if (updates == null)
            throw new ArgumentNullException(nameof(updates));

        foreach (var update in updates)
        {
            SetPropertyValue(entity, update.Key, update.Value);
        }
    }

    public string Export(IEnumerable<T> items, string[] propertiesToExport)
    {
        if (items == null)
            throw new ArgumentNullException(nameof(items));
        if (propertiesToExport == null || propertiesToExport.Length == 0)
            throw new ArgumentException("No properties specified for export", nameof(propertiesToExport));

        var txt = new StringBuilder();
        foreach (var item in items)
        {
            var values = propertiesToExport
                .Select(prop => GetPropertyValue(item, prop)?.ToString())
                .ToList();
            txt.AppendLine(string.Join(",", values));
        }
        return txt.ToString();
    }

    public void CopyMatchingProperties<TTarget>(T source, TTarget target) where TTarget : class
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));
        if (target == null)
            throw new ArgumentNullException(nameof(target));

        var targetAccessor = new DynamicPropertyAccessor<TTarget>();

        foreach (var prop in _properties.Values)
        {
            try
            {
                if (targetAccessor.HasProperty(prop.Name))
                {
                    var value = GetPropertyValue(source, prop.Name);
                    targetAccessor.SetPropertyValue(target, prop.Name, value);
                }
            }
            catch (Exception ex)
            {
                // 可以选择记录日志
                Console.WriteLine($"Error copying property {prop.Name}: {ex.Message}");
            }
        }
    }

    public (bool IsValid, List<string> MissingProperties) ValidateRequiredProperties(
        T entity,
        string[] requiredProperties)
    {
        if (entity == null)
            throw new ArgumentNullException(nameof(entity));

        if (requiredProperties == null || requiredProperties.Length == 0)
            return (true, new List<string>());

        var missingProperties = new List<string>();

        foreach (var propName in requiredProperties)
        {
            if (!HasProperty(propName))
            {
                missingProperties.Add($"Property '{propName}' does not exist");
                continue;
            }

            var value = GetPropertyValue(entity, propName);
            if (value == null)
            {
                missingProperties.Add(propName);
            }
        }

        return (missingProperties.Count == 0, missingProperties);
    }

}


class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

internal class Program
{
    static void Main(string[] args)
    {
        try
        {
            var accessor = new DynamicPropertyAccessor<Product>();
            var product = new Product { Id = 1, Name = "Laptop", Price = 999.99m };
            var product2 = new Product();

            // 测试属性访问
            Console.WriteLine($"产品名称: {accessor.GetPropertyValue(product, "Name")}");
            accessor.SetPropertyValue(product, "Price", 1099.99m);
            Console.WriteLine($"更新后的价格: {product.Price}");

            // 测试属性验证
            var requiredNameField = new[] { "Name" };
            var (isValidName, missingName) = accessor.ValidateRequiredProperties(product, requiredNameField);
            if (!isValidName)
            {
                Console.WriteLine($"缺少必要字段: {string.Join(", ", missingName)}");
            }

            var requiredSizeField = new[] { "Size" };
            var (isValidSize, missingSize) = accessor.ValidateRequiredProperties(product, requiredSizeField);
            if (!isValidSize)
            {
                Console.WriteLine($"缺少必要字段: {string.Join(", ", missingSize)}");
            }

            // 测试批量更新
            var updates = new Dictionary<string, object>
            {
                { "Name", "New Laptop" },
                { "Price", 1299.99m }
            };
            accessor.BatchUpdateProperties(product, updates);
            Console.WriteLine($"批量更新后 - 名称: {product.Name}, 价格: {product.Price}");

            // 测试属性复制
            accessor.CopyMatchingProperties(product, product2);
            Console.WriteLine($"复制后的产品 - Id: {product2.Id}, Name: {product2.Name}, Price: {product2.Price}");

            // 测试导出
            var products = new List<Product> { product, product2 };
            var export = accessor.Export(products, new[] { "Id", "Name", "Price" });
            Console.WriteLine("\n导出结果:");
            Console.WriteLine(export);

            // 测试属性信息
            Console.WriteLine("\n属性信息:");
            foreach (var propName in accessor.GetPropertyNames())
            {
                var propType = accessor.GetPropertyType(propName);
                var isWritable = accessor.IsPropertyWritable(propName);
                Console.WriteLine($"属性: {propName}, 类型: {propType.Name}, 可写: {isWritable}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"发生错误: {ex.Message}");
        }

        Console.WriteLine("\n按任意键退出...");
        Console.ReadKey();
    }
}

运行结果

产品名称: Laptop
更新后的价格: 1099.99
缺少必要字段: Property 'Size' does not exist
批量更新后 - 名称: New Laptop, 价格: 1299.99
复制后的产品 - Id: 1, Name: New Laptop, Price: 1299.99

导出结果:
1,New Laptop,1299.99
1,New Laptop,1299.99


属性信息:
属性: Id, 类型: Int32, 可写: True
属性: Name, 类型: String, 可写: True
属性: Price, 类型: Decimal, 可写: True

应用场景举例

这种实现特别适用于:

  • 需要动态处理对象属性的场景
  • ORM(对象关系映射)框架
  • 通用数据转换工具
  • 配置管理系统
  • 单元测试框架

使用场景:

  • 数据映射:在不同对象之间复制属性值
  • 通用数据处理:处理具有动态属性的对象
  • 配置系统:动态读写配置值
  • 序列化/反序列化:动态处理对象属性

数据映射和转换

// 不同对象间的属性复制
public class SourceDTO
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class TargetEntity
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}
internal class Program
{
    static void Main(string[] args)
    {      
        var dto = new SourceDTO { Name = "Test", Price = 100 };
        var entity = new TargetEntity();
        var sAccessor = new DynamicPropertyAccessor<SourceDTO>();
        var tAccessor = new DynamicPropertyAccessor<TargetEntity>();
        sAccessor.CopyMatchingProperties(dto, entity);

        var targetEntities = new List<TargetEntity> { entity };
        var export = tAccessor.Export(targetEntities, new[] { "Name", "Price" });
        Console.WriteLine("\n导出结果:");
        Console.WriteLine(export);
        Console.ReadKey();
    }
}
导出结果:
Test,100

属性复制

类似数据映射和转换,见完整样例

配置系统

// 从配置文件动态设置对象属性
public class AppSettings
{
    public string DatabaseConnection { get; set; }
    public int Timeout { get; set; }
    public bool EnableLogging { get; set; }
}
internal class Program
{
    // 模拟从配置文件读取配置
    public static Dictionary<string, object> LoadConfigurationFromFile()
    {
        return new Dictionary<string, object>
            {
                { "DatabaseConnection", "Config.ConnectionString" },
                { "Timeout", 30 },
                { "EnableLogging", true },
            };
    }
    static void Main(string[] args)
    {
        // 使用示例
        var settings = new AppSettings();
        var configValues = LoadConfigurationFromFile(); // 返回 Dictionary<string, object>
        var accessor = new DynamicPropertyAccessor<AppSettings>();
        accessor.BatchUpdateProperties(settings, configValues);

        var targetEntities = new List<AppSettings> { settings };
        var export = accessor.Export(targetEntities, new[] { "DatabaseConnection", "Timeout", "EnableLogging" });
        Console.WriteLine("\n导出结果:");
        Console.WriteLine(export);
        Console.ReadKey();
    }
}

数据导入导出

// CSV数据导入
public void ImportFromCsv(string csvData)
{
    var accessor = new DynamicPropertyAccessor<Product>();
    foreach (var line in csvData.Split('\n'))
    {
        var values = line.Split(',');
        var product = new Product();
        accessor.SetPropertyValue(product, "Name", values[0]);
        accessor.SetPropertyValue(product, "Price", decimal.Parse(values[1]));
        // 保存产品...
    }
}

数据验证

见完整样例

通用数据处理工具

// 通用数据比较器
public bool CompareObjects<T>(T obj1, T obj2, string[] propertiesToCompare) where T : class
{
    var accessor = new DynamicPropertyAccessor<T>();
    foreach (var prop in propertiesToCompare)
    {
        var value1 = accessor.GetPropertyValue(obj1, prop);
        var value2 = accessor.GetPropertyValue(obj2, prop);
        if (!Equals(value1, value2)) return false;
    }
    return true;
}

特点

  • 灵活性:可以在运行时动态访问和修改对象属性
  • 通用性:适用于任何类型的对象
  • 性能优化:缓存属性信息,避免重复反射
  • 功能丰富:提供批量操作、验证、复制等功能

注意事项

  • 性能考虑:虽然有缓存,但反射操作仍比直接访问慢
  • 属性可访问性:注意属性的读写权限

参考

  • C#: 使用反射和泛型的高级技巧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/908881.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大数据计算里的-Runtime Filter

文章目录 Runtime Filter示例 Runtime Filter 从纸面意义来看&#xff0c;就是程序在运行时&#xff0c;根据实际的数据去进行一些过滤操作。这和静态的规则优化不同&#xff0c;静态优化不考虑实现的数据的分布。 示例 select a.* ,b.* a join b on a.idb.id假设一下数据…

SD教程 ControlNet 之MLSD 室内设计

你是否已经厌倦了传统的室内设计方式&#xff0c;想探索新方法来增强作品设计感&#xff1f;本期小编就同大家分享一个新武器&#xff0c;用Stable Diffusion的ControlNet来打造一个室内设计全新工作流。无论你是经验丰富的室内设计师还是初学小白&#xff0c;都将使你的日常工…

蓬勃发展:移动开发——关于软件开发你需要知道些什么

一、前言 移动开发一直都是软件开发领域中最有趣的领域之一&#xff0c;这是因为&#xff1a; 1、移动开发为“只有一个人”的开发团队提供了一个非常独特的机会&#xff0c;让他可以在相对较短的时间内建立一个实际的、可用的、有意义的应用程序&#xff1b; 2、移动开发也代…

性能测试 —— MySQL性能测试方案设计!

01、慢查询 查看是否开启慢查询 mysql> show variables like %slow%’; 如图所示&#xff1a; 系统变量log_slow_admin_statements 表示是否将慢管理语句例如ANALYZE TABLE和ALTER TABLE等记入慢查询日志 启用log_slow_extra系统变量 &#xff08;从MySQL 8.0.14开始提…

每日OJ题_牛客_爱吃素_数学_C++_Java

目录 牛客_爱吃素_数学 题目解析 C代码 Java代码 牛客_爱吃素_数学 爱吃素 (nowcoder.com) 描述&#xff1a; 牛妹是一个爱吃素的小女孩&#xff0c;所以很多素数都害怕被她吃掉。 一天&#xff0c;两个数字aaa和bbb为了防止被吃掉&#xff0c;决定和彼此相乘在一…

Vue 自定义icon组件封装SVG图标

通过自定义子组件CustomIcon.vue使用SVG图标&#xff0c;相比iconfont下载文件、重新替换更节省时间。 子组件包括&#xff1a; 1. Icons.vue 存放所有SVG图标的path 2. CustomIcon.vue 通过icon的id索引对应的图标 使用的时候需要将 <Icons></Icons> 引到使用的…

MySQL-如果你在添加外键时忘加约束名,如何找到系统默认的约束名

问题 在你添加约束的时候&#xff0c;一般都会为其取名以方便后期的修改&#xff0c;但是如果你忘记了呢&#xff0c;如何找到系统默认的约束名 解决方法 -- 查找约束名 SELECTCONSTRAINT_NAME FROMINFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERETABLE_NAME emp ANDREFERENCED_T…

KP8530X系列KP85301SGA 650V耐压 集成自举二极管的半桥栅极驱动器 北欧双线灯调色解决方案

KP8530X同系列选型参考&#xff1a; KP85301SGA兼容IR2103 KP85302SGA兼容IR2106、IR2001、IR2005、IR2186 KP85303SGA兼容IR2104 KP85304SGA兼容IR2304 KP85305SGA兼容IR21857 KP8530X系列KP85301SGA是一款 650V 耐压&#xff0c;集成自举二极管的半桥栅极驱…

IPC-A-610J-中文版 CHINESE-中文版 2024 电子组件的可接受性

IPC-A-610J-中文版 CHINESE-中文版 2024 电子组件的可接受性.pdf 链接: https://pan.baidu.com/s/1UreAzlB_P7tGH_WoFL2Ybg?pwd1234 提取码: 1234 https://share.weiyun.com/eQCyAPYh 通过网盘分享的文件&#xff1a;IPC-A-610J-中文版 CHINESE-中文版 2024 电子组件的可接受性…

JAVA设计模式之【建造者模式】

1 定义 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 2 类图 产品类&#xff08;Product&#xff09;&#xff1a;表示被创建的复杂…

SQL server 列转行

在 SQL Server 中&#xff0c;将列转换为行的操作通常被称为“透视”&#xff08;Pivot&#xff09;的逆操作&#xff0c;即“反透视”&#xff08;Unpivot&#xff09;。SQL Server 提供了 UNPIVOT 关键字来实现这一功能。假设你有一个表 EmployeeDetails&#xff0c;其中包含…

分类算法——决策树 详解

决策树的底层原理 决策树是一种常用的分类和回归算法&#xff0c;其基本原理是通过一系列的简单决策&#xff0c;将数据集划分为多个子集&#xff0c;从而实现分类。决策树的核心思想是通过树形结构表示决策过程&#xff0c;节点代表特征&#xff0c;边代表决策&#xff0c;叶子…

【小白学机器学习31】 大数定律,中心极限定理,标准正态分布与概率的使用

目录 1 正态分布相关的2个相关定理 1.1 大数定律&#xff1a;(证明了)分布的稳定性 1.2 中心极限定理&#xff1a;(证明了)分布的收敛性 2 使用标准差和概率的2种思路 2.1 标准正态分布的曲线 2.2 两种使用方式 2.3 第1种&#xff1a;按整数倍标准差δ 作为标准使用 2.…

Sourcetree 启动问题(完美解决)

概述 sourcetree 之所以打开就闪退&#xff0c;是因为没有关闭 sourcetree 关机或者系统自动更新等没有关闭sourcetree就直接关机的行为导致缓存信息不匹配&#xff0c;删除的目的是为了重新加载缓存。 下方直接上图&#xff0c;按顺序操作&#xff1a; 1.找到 sourceTree 安…

Sigrity Power SI VR noise Metrics check模式如何进行电源噪声耦合分析操作指导

SSigrity Power SI VR noise Metrics check模式如何进行电源噪声耦合分析操作指导 Sigrity Power SI的VR noise Metrics check模式本质上是用来评估和观测器件的电源网络的耦合对于信号的影响,输出S参数以及列出具体的贡献值。 以下图为例

安科瑞Acrel-2000ES储能柜能量管理系统的详细介绍-安科瑞 蒋静

Acrel-2000ES储能柜能量管理系统具备全面的储能监控和管理功能。它包括了储能系统设备&#xff08;如PCS、BMS、电表、消防、空调等&#xff09;的详细信息&#xff0c;并实现了数据采集、处理、存储、数据查询与分析、可视化监控、报警管理和统计报表等功能。此外&#xff0c;…

细腻的链接:C++ list 之美的解读

细腻的链接&#xff1a;C list 之美的解读 前言&#xff1a; 小编在前几日刚写过关于vector容器的内容&#xff0c;现在小编list容器也学了一大部分了&#xff0c;小编先提前说一下学这部分的感悟&#xff0c;这个部分是我学C以来第一次感到有难度的地方&#xff0c;特别是在…

百度集度嵌入式面试题及参考答案

linux 系统之间通信机制有哪些&#xff1f; Linux 系统之间存在多种通信机制&#xff0c;以下是一些常见的通信机制及其详细介绍。 管道&#xff08;Pipe&#xff09; 原理&#xff1a;管道是一种半双工的通信方式&#xff0c;数据只能单向流动。它基于文件描述符&#xff0c;在…

使用vite+react+ts+Ant Design开发后台管理项目(五)

前言 本文将引导开发者从零基础开始&#xff0c;运用vite、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈&#xff0c;构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导&#xff0c;文章旨在为开发者揭示如何利用这些技术…

MATLAB计算朗格朗日函数

1. 朗格朗日函数介绍 朗格朗日函数&#xff08;Lagrange function&#xff09;通常用于优化问题&#xff0c;尤其是带有约束的优化问题。其一般形式为&#xff1a; 其中&#xff1a; f(x) 是目标函数。 是约束条件。 是拉格朗日乘子。 为了编写一个MATLAB代码来计算和绘制…