在工作中看到
不使用反射进行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); } } } }