採用Delegate對一個未知類型的對象進行"遍歷"

文章閱讀順序建議:
本系列有一個遞進的順序,可依次閱讀以下的文章:
一、採用Delegate對一個未知類型的對象進行"遍歷"
二、採用表達式樹(Expression Tree)對一個對象的屬性進行“遍歷”
三、 採用表達式樹(Expression Block)對一個對象的屬性進行“遍歷”



由於有時候需要對一個不知類型的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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章