采用Delegate对一个未知类型的对象进行"遍历"

来源:互联网 发布:好看的末世小说知乎 编辑:程序博客网 时间:2024/05/01 14:58

文章阅读顺序建议:
本系列有一个递进的顺序,可依次阅读以下的文章:
一、采用Delegate对一个未知类型的对象进行"遍历"
http://blog.csdn.net/kmguo/article/details/17392185
二、采用表达式树(Expression Tree)对一个对象的属性进行“遍历”
http://blog.csdn.net/kmguo/article/details/19975331
三、 采用表达式树(Expression Block)对一个对象的属性进行“遍历”
 http://blog.csdn.net/kmguo/article/details/20376187



由于有时候需要对一个不知类型的object进行“遍历”,得到它的所有公有的属性和字段。虽然可以采用反射来实现,但是代价太高了,特别是要多次执行时。因此,我写了一个用Delegate来实现的方法,供大家参考和交流。

我定义了MemberAccessor.dll。里面有四个文件,具体的内容如下:

定义一个获得对象的某个Member的Value的接口:IMemberAccessor.cs

namespace MemberAccessor{    /// <summary>    /// 获得Member值的接口    /// </summary>    public interface IMemberAccessor    {        object GetValue(object instance);    }}



定义一个获取指定类型的指定属性的GetValue的类: PropertyDelegateAccessor.cs
using System;using System.Reflection;namespace MemberAccessor{    /// <summary>    /// 针对PropertyInfo,生成一个能够获得PropertyInfo Value的实例    /// </summary>    /// <typeparam name="TType"></typeparam>    /// <typeparam name="TMember"></typeparam>    public class PropertyDelegateAccesstor<TType, TMember> : IMemberAccessor    {        private Func<TType, TMember> m_getValueDelegate;        public PropertyDelegateAccesstor(Type type, MethodInfo methodInfo)        {            m_getValueDelegate =                (Func<TType, TMember>) Delegate.CreateDelegate(typeof (Func<TType, TMember>), null, methodInfo);        }         public object GetValue(object instance)        {            return m_getValueDelegate((TType)instance);        }    }}



定义一个获取指定类型的指定字段的GetValue的类:FieldDelegateAccessor
using System;using System.Reflection;namespace MemberAccessor{    /// <summary>    /// 提交一个FieldInfo,生成一个获得该FieldInfo Value的实例    /// </summary>    public class FieldDelegateAccessor:IMemberAccessor    {        private Func<object, object> m_getValueDelegate;         public FieldDelegateAccessor(FieldInfo fieldInfo)        {            m_getValueDelegate = fieldInfo.GetValue;        }        public object GetValue(object instance)        {            return m_getValueDelegate(instance);        }    }}



利用上面的三个接口与类,生成一个针对指定类型的所有属性与字段访问的类:InstanceDelegateAccessor.cs
using System;using System.Collections.Generic;using System.Reflection;namespace MemberAccessor{    /// <summary>    /// 针对某一个类型的实例,生成一个获得该类型的相应Property与Field 值的实例    /// </summary>    public class InstanceDelegateAccessor    {        private Dictionary<string, IMemberAccessor> m_memberAccessors;        public InstanceDelegateAccessor(Type type, BindingFlags propertyBindingFlags, BindingFlags fieldBindingFlags)        {            m_memberAccessors = new Dictionary<string, IMemberAccessor>();            #region create accessor            //property            var pis = type.GetProperties(propertyBindingFlags);            foreach (var pi in pis)            {                var name = pi.Name;                var accessor =                    Activator.CreateInstance(                        typeof (PropertyDelegateAccesstor<,>).MakeGenericType(type, pi.PropertyType), type, pi.GetGetMethod()) as                    IMemberAccessor;                m_memberAccessors.Add(name, accessor);            }            //field            var fis = type.GetFields(fieldBindingFlags);            foreach (var fi in fis)            {                var name = fi.Name;                var accessor = new FieldDelegateAccessor(fi);                                   m_memberAccessors.Add(name, accessor);            }            #endregion        }            public object GetValue(object instance, string memberName)        {            if (instance == null || string.IsNullOrWhiteSpace(memberName))            {                throw new ArgumentNullException("input paremeters have null value.");            }            IMemberAccessor accessor = null;            if (m_memberAccessors.TryGetValue(memberName, out accessor) == false || accessor == null)            {                throw new ArgumentException("can not find any MemberDelegate for memberName:" + memberName);            }            return accessor.GetValue(instance);        }    }      /// <summary>        /// 返回一个类的所有字段,属性的序列对:        /// Prop: Value        /// Field: Value        /// </summary>        /// <param name="instance"></param>        /// <returns></returns>        public IList<Tuple<object,object>> GetValues(object instance)        {            var objs = new List<Tuple<object, object>>();            foreach (var key in m_memberAccessors.Keys)            {                var value = GetValue(instance, key);                objs.Add(new Tuple<object, object>(key, value));            }            return objs;        }}


最后,测试:
说明:
    在Main方法里先创建两个对象,一个是StringTest,另一个是IntTest。并创建与它们类型相对应的InstanceDelegateAccessor对象。
最后分别调用ShowAllPublicValue,将对象和它对应的InstanceDelegateAccessor对象作为参数传入,再在该方法里打印对应的属性与字段。
using System;using System.Reflection;using MemberAccessor;namespace Study{    public class Test    {        public string stringField;        public int IntProperty { get; set; }        public Test(int prop )        {            stringField = "stringField value";            IntProperty = prop;        }    }     public class Program    {        public static void Main(string[] args)        {            InstanceDelegateAccessor ida = null;            int total = 10; //多个相同的类,采用同一个Serializer即可            for (int i = 0; i < total; ++i)            {                //新建的Object                Test test = new Test(i);                if (ida == null)                {                    //没有Serializer,新建                    ida = new InstanceDelegateAccessor(test.GetType(), BindingFlags.Public | BindingFlags.Instance,                                                       BindingFlags.Instance | BindingFlags.Public);                }                                //返回的结果:                var results = ida.GetValues(test);                foreach (var result in results)                {                    Console.WriteLine(result.Item1 + ": " + result.Item2);                }                Console.WriteLine();            }        }    }}



有一点不足的是,如果一个类里引用了另一个类的对象,这样的话,就只能调用默认的ToString函数了,而不是这个对象的属性了。
比如说:
class A{
    public B  b;
}

class B{
    public int Bid{get;set;}
}

则一旦我去获取A的所有字段,那么对于A里的b对象,我就只能得到b的ToString()方法返回的结果,我无法得到B里的属性Bid。


有知道如何获得的朋友,希望交流。


参考:http://www.cnblogs.com/nankezhishi/archive/2012/02/11/dynamicaccess.html#di


0 0