`

改进篇《不使用反射进行C#属性的运行时动态访问》

阅读更多

 在工作中看到

不使用反射进行C#属性的运行时动态访问:

http://www.cnblogs.com/nankezhishi/archive/2012/02/11/dynamicaccess.html

这篇文章后觉得很不错!但是在运用其代码的过程中也发现了这个代码存在的一些bug,经过努力,已经把它fix掉了,现在分享我修改后的代码:
Dictionary只放存在的类和属性的 GET、SET委托:
<key         ,  Value>
<类+属性名,  对应的GET、SET委托>
如果类名或者属性名不存在,则不会给添加到这个单列的Dictionary中。

1.修改了在PropertyAccessor构造时抛出找不到GET SET Method的异常。
此异常的原因是因为有些property没有public Get 或者Set方法,导致propertyInfo.GetGetMethod()/GetSetMethod() 时返回null,继而导致Delegate .CreateDelegate创建失败。
2.增加了一个抛异常的辅助类,增强了异常处理机制。
3.将MemberAccessor 设计成为单列模式,增加性能,方便调用。
4.经过测试,没有引入其他bug。
using System;
using System.Collections.Generic;
using System.Reflection;

namespace XXX.Common
{
    internal interface INamedMemberAccessor
    {
        object GetValue(object instance);
        void SetValue(object instance, object newValue);
    }

    /// <summary>
    /// Abstraction of the function of accessing member of a object at runtime.
    /// </summary>
    public interface IMemberAccessor
    {
        /// <summary>
        /// Get the member value of an object.
        /// </summary>
        /// <param name="instance">The object to get the member value from.</param>
        /// <param name="memberName">The member name, could be the name of a property of field. Must be public member.</param>
        /// <returns>The member value</returns>
        object GetValue(object instance, string memberName);

        /// <summary>
        /// Set the member value of an object.
        /// </summary>
        /// <param name="instance">The object to get the member value from.</param>
        /// <param name="memberName">The member name, could be the name of a property of field. Must be public member.</param>
        /// <param name="newValue">The new value of the property for the object instance.</param>
        void SetValue(object instance, string memberName, object newValue);
    }

    internal class PropertyAccessor<T, P> : INamedMemberAccessor
    {
        private Func<T, P> m_GetValueDelegate;
        private Action<T, P> m_SetValueDelegate;

        public PropertyAccessor(PropertyInfo propertyInfo)
        {
            Guard.ArgumentNotNull(propertyInfo, "Property can't be null");

            var getMethodInfo = propertyInfo.GetGetMethod();
            if (null != getMethodInfo)
            {
                m_GetValueDelegate = (Func<T, P>)Delegate.CreateDelegate(typeof(Func<T, P>), getMethodInfo);
            }

            var setMethodInfo = propertyInfo.GetSetMethod();
            if (null != setMethodInfo)
            {
                m_SetValueDelegate = (Action<T, P>)Delegate.CreateDelegate(typeof(Action<T, P>), setMethodInfo);
            }
        }

        public object GetValue(object instance)
        {
            Guard.ArgumentNotNull(m_GetValueDelegate, "The Property doesn't have GetMethod");
            return m_GetValueDelegate((T)instance);
        }

        public void SetValue(object instance, object newValue)
        {
            Guard.ArgumentNotNull(m_SetValueDelegate, "The Property doesn't have SetMethod");
            m_SetValueDelegate((T)instance, (P)newValue);
        }
    }

    /// <summary>
    /// Singleton, MemberAccessor used to accessing member of a object at runtime.
    /// </summary>
    public class MemberAccessor : IMemberAccessor
    {
        #region Singleton
        private MemberAccessor() { }
        public static MemberAccessor Instance
        {
            get { return Nested.m_instance; }
        }
        private class Nested
        {
            static Nested() { }
            internal static readonly MemberAccessor m_instance = new MemberAccessor();
        }
        #endregion

        private static Dictionary<string, INamedMemberAccessor> m_accessorCache = new Dictionary<string, INamedMemberAccessor>();

        /// <summary>
        /// Get the member value of an object.
        /// </summary>
        /// <param name="instance">The object to get the member value from.</param>
        /// <param name="memberName">The member name, could be the name of a property of field. Must be public member.</param>
        /// <returns>The member value</returns>
        public object GetValue(object instance, string memberName)
        {
            INamedMemberAccessor ma = FindAccessor(instance, memberName);
            Guard.ArgumentNotNull(ma, "The instance doesn't have this property");
            return ma.GetValue(instance);
        }

        /// <summary>
        /// Set the member value of an object.
        /// </summary>
        /// <param name="instance">The object to get the member value from.</param>
        /// <param name="memberName">The member name, could be the name of a property of field. Must be public member.</param>
        /// <param name="newValue">The new value of the property for the object instance.</param>
        public void SetValue(object instance, string memberName, object newValue)
        {
            INamedMemberAccessor ma = FindAccessor(instance, memberName);
            Guard.ArgumentNotNull(ma, "The instance doesn't have this property");
            ma.SetValue(instance, newValue);
        }

        private INamedMemberAccessor FindAccessor(object instance, string memberName)
        {
            Type type = instance.GetType();
            string key = type.FullName + memberName;

            INamedMemberAccessor accessor = null;
            if (!m_accessorCache.TryGetValue(key, out accessor))
            {
                #region bug fix from Ambiguous Match Exception
                PropertyInfo propInfo = type.GetProperty(memberName, BindingFlags.DeclaredOnly |
                                    BindingFlags.Public | BindingFlags.NonPublic |
                                    BindingFlags.Instance);
                if (null == propInfo)
                {
                    propInfo = type.GetProperty(memberName);
                }
                #endregion
                if (null == propInfo)
                {
                    return null;
                }
                else
                {
                    accessor = Activator.CreateInstance(typeof(PropertyAccessor<,>).MakeGenericType(type, propInfo.PropertyType), propInfo) as INamedMemberAccessor;
                    m_accessorCache.Add(key, accessor);
                }
            }
            return accessor;
        }
    }
}
   
using System;

namespace XXX.Common
{
    /// <summary>
    /// Common guard clauses
    /// </summary>
    public static class Guard
    {
        /// <summary>
        /// Checks an argument to ensure it isn't null
        /// </summary>
        /// <param name="argumentValue">The argument value to check.</param>
        /// <param name="argumentName">The name of the argument.</param>
        public static void ArgumentNotNull(object argumentValue, string argumentName)
        {
            if (argumentValue == null)
            {
                throw new ArgumentNullException(argumentName);
            }
        }
    }
}

 

分享到:
评论

相关推荐

    c#遍历obj属性用例

    在C#编程中,对象属性的遍历是一个常见的操作,尤其是在需要动态处理对象或进行反射操作时。根据给定的文件信息,我们将深入探讨如何在C#中遍历对象的属性,以及理解其中的关键概念。 ### 核心知识点:C#中的属性...

    C#使用反射做成的简单框架

    **反射**是.NET Framework提供的一种强大功能,它允许运行时的代码获取类型信息并操作这些类型,包括创建对象、调用方法和访问字段。通过使用`System.Reflection`命名空间中的类,我们可以动态地加载、检查和交互与...

    C#高效编程改进C#代码的50个行之有效的办法( 第2版) 中文版

    22. **掌握反射(Reflection)**:反射可以动态地获取和操作类型信息,为运行时代码提供灵活性。 23. **运用Structs和Classes的适当选择**:根据性能和内存需求,选择使用值类型(struct)还是引用类型(class)。 ...

    archive_C#反射工厂演示程序包.zip.zip

    1. **C#反射**:C#的System.Reflection命名空间提供了对类型、属性、方法等元数据的访问,使得代码能够在运行时获取类的信息,实例化对象,甚至动态调用方法。在游戏开发中,反射可能用于动态加载资源(如地图、角色...

    展现C#展现C#

    9. **元编程和反射**:C#支持反射,可以在运行时检查和操作程序集、类型、方法等,实现元编程。 10. **属性和事件**:C#中的属性提供了一种封装类成员的方式,而事件则用于处理组件间的通信,遵循发布/订阅模式。 ...

    深入理解c#英文版

    5. **属性与事件**:C#中,属性用于封装字段,提供访问控制和验证,而事件则处理对象间的通信。深入理解这两者对于构建健壮的用户界面至关重要。 6. **委托与事件**:委托是C#中的函数指针,可以用来实现回调和事件...

    C#本质论 第4版 C#5.0.pdf

    此外,书中还涵盖了异常处理、反射、多线程、网络编程、XML处理、数据库访问等多个领域,旨在培养出能够熟练运用C#5.0进行软件开发的专业程序员。 总之,《C#本质论 第4版 C#5.0》是C#开发者必备的参考书籍,无论你...

    CLR via C#, 4th Edition英文版第四版带目录

    CLR中的元数据提供了程序集、类型、方法等信息,反射则允许在运行时动态访问这些信息,为创建高度灵活和可扩展的系统提供了可能。 6. **并行与并发** 随着多核处理器的普及,理解和掌握并行和并发编程变得越来越...

    C#编程指南 最新

    C# 6.0不仅在语言层面进行了许多改进,还加强了与.NET Framework的集成,使得开发者能够更加高效地开发高质量的应用程序。通过深入理解这些核心概念和技术要点,开发者将能够在实际开发中更加得心应手。

    [C_#4.0本质论(第3版)].(Essential.C#4.0).Mark.Michaelis.文字版

    2. **动态类型**:C# 4.0引入了`dynamic`关键字,允许在运行时进行类型绑定,使得与动态语言的交互变得更加便捷,如与Python或JavaScript等的集成。 3. **多线程和并发**:C# 4.0提供了Task Parallel Library (TPL)...

    c# 4.0新特性详解

    动态方法、属性的访问实际上被转化为`GetMember`、`Invoke`等方法的调用。 除了动态类型,C# 4.0还有其他一些增强,比如改进了委托和事件的多播处理,引入了命名和可选参数,以及更灵活的数组和集合初始化等。这些...

    C#

    16. **元数据与反射**:C#支持元数据,允许在运行时获取类型信息,反射则用于在运行时动态创建对象、调用方法等。 17. **异步流**:在C# 8.0中引入的新特性,使得处理大量数据时能保持低内存占用和高吞吐量。 18. ...

    C# 4.0完全参考手册

    - **反射和元数据**:增强了程序集、类型和成员的动态访问能力。 总之,《C# 4.0完全参考手册》涵盖了C#编程语言的各个方面,是开发者深入学习和掌握C# 4.0的宝贵资源,无论你是初学者还是经验丰富的开发者,都能...

    C# 反射与dynamic最佳组合示例代码

    总结一下,C#中的反射与`dynamic`结合使用,可以实现更灵活的代码编写,特别是在处理动态对象创建、动态调用方法和访问属性时。反射允许我们在运行时探索和操作类型,而`dynamic`则简化了动态类型绑定的过程。在设计...

    C#语言参考

    反射机制利用这些元数据,可以在运行时动态地创建对象、调用方法等。 11. **多线程**:C#提供了System.Threading命名空间,包含丰富的多线程支持,如线程池、同步原语等。 12. **并发支持**:C# 5.0及以后版本引入...

    C#编程语言详解 内容挺详细的

    - 元数据描述程序集和类型的信息,反射则允许运行时动态地获取这些信息并进行操作。 11. **异构计算**: - 支持并行计算和GPU编程,如C#的Parallel Extensions和Compute Shader。 12. **最新特性**: - C# 8.0...

    Effective_C#中文版_改善C#程序的50种方法.pdf

    - **核心思想**:提倡使用属性来代替直接访问类的成员变量,这有助于封装和控制对数据的访问。 - **应用场景**: - 当需要改变数据存储方式或增加逻辑处理时,如添加验证逻辑、日志记录等。 - 提供更灵活的接口,...

    Professional C# 5.0 重點

    1.4.3 反射:程序集支持反射,允许在运行时动态地获取类型信息,创建对象,调用方法等。 1.4.4 并行编程:.NET框架提供并行编程工具,如Task Parallel Library (TPL),使得开发者能轻松地利用多核处理器的优势。 ...

    C#本质论 Essential C# 3.0

    - **反射与动态调用**:解释如何运行时访问和调用类型的信息。 - **异常处理**:深入理解异常处理机制,包括如何预防和处理常见的异常。 ##### 语言集成查询 (LINQ) - **LINQ to Objects**:介绍如何使用LINQ查询...

Global site tag (gtag.js) - Google Analytics