1. 程式人生 > >c#動態型別,及動態物件的建立,合併2個物件,map

c#動態型別,及動態物件的建立,合併2個物件,map

經常會遇到這樣的情況,我們在響應客戶端請求的資料的時候需要對資料進行處理,比如資料庫中的資料是int型,它可能表示某個列舉,或者其它的邏輯意義(資料庫這樣的設計可能是從資料安全性、儲存量上等角度考慮),但是客戶端顯示的時候需要是它具體的意義。這個時候我們的處理方式一般都是2中的,如果邏輯不復雜,且單一的話,直接修改sql語句就能處理好資料來源,這個時候程式碼裡面不需要處理什麼。但是如果邏輯復稍許複雜或者判斷的情況有很多分支,我們不得不從程式碼角度來處理了。單個物件還好,多個物件比如是個list<T>,那就要迴圈對某個物件的欄位進行XXX了。進而衍生出了這就出現了DTO,Arg的中間物件,當然,我個人是很喜歡這樣的設計的,但是某些時候也會偷懶不想寫(多半情況我直接寫程式碼生器批量生產),比如在測試的時候,在接私活的時候,在演示的時候,只為快速呈現想要的效果 都懶得去屑,是的,你會說市面上不是有很多的map庫,比如automap,tinymap,甚至json.net裡面的動態特性重寫,方法當然很多,但用一個大輪子來費力搞這麼個小事,我覺得划不來。且輪子越來越大它要乾的事越多,我可不想搞的那麼複雜,嗯,就是這樣,寫了個。具體的程式碼貼到下面,如果看明白,會很方便的擴充套件了或修改成自己想要的效果。

using System.Dynamic;
using System.Reflection;
using System.Collections.Concurrent;

private static readonly ConcurrentDictionary<RuntimeTypeHandle, PropertyInfo[]>
   DynamicObjectProperties = new ConcurrentDictionary<RuntimeTypeHandle, PropertyInfo[]>();

private IDictionary<string, Object> ToDynamicResult<T>(T classobj, Func<string, object, object> injectAct)
    where T : IInjectClass, new()
{
    var type = typeof(T);
    var key = type.TypeHandle;
    var dynamicResult = new ExpandoObject() as IDictionary<string, Object>;

    PropertyInfo[] queryPts = null;
    DynamicObjectProperties.TryGetValue(key, out queryPts);

    if (queryPts == null)
    {
        queryPts = type.GetProperties();
        DynamicObjectProperties.TryAdd(key, queryPts);
    }

    foreach (var p in queryPts)
    {
        var attributes = p.GetCustomAttributes(typeof(IngorePropertyAttribute), true);
        var columnMapping = attributes.FirstOrDefault();
        if (columnMapping != null) continue;

        var _name = p.Name;
        var _value = p.GetValue(classobj, null);
        object _tempvalue = _value;

        if (injectAct != null) _tempvalue = injectAct.Invoke(_name, _value);

        //var value = Convert.ChangeType(value,typeof(string));  
        dynamicResult.Add(p.Name, _tempvalue);
    }

    return dynamicResult;
}

/// <summary>
/// 支援動態輸出的物件介面
/// </summary>
public interface IInjectClass
{
}
/// <summary>
/// 動態輸出時忽略此標記的屬性
/// </summary>
public class IngorePropertyAttribute : Attribute
{
}
下面我們測試一個:
 public class kk : IInjectClass
 {
    public string aa { get; set; }
    public int bb { get; set; }
    [IngoreProperty]
    public bool cc { get; set; }
    public DateTime dd { get; set; }
 }
kk ist = new kk();
ist.aa = "aaa";
ist.bb = 123;
ist.cc = false;
ist.dd = DateTime.Now;

var tt = ToDynamicResult<kk>(ist, (k, v) =>
{
    if (k != "aa") return v;

    return v + "(改變了哦)";
});

var json = Tools.JsonUtils.JsonSerializer(tt);

json = json + "<br /><br />" + Tools.JsonUtils.JsonSerializer(ToDynamicResult<kk>(
       new kk
       {
           aa = "test",
           bb = 789,
           cc = true,
           dd = DateTime.Now.AddDays(2)
       }, null));

Response.Write(json);

您可以重新構造帶引數的特性或者修改injectAct物件,改成適合自己的

下面寫個測試,改成表示式樹最好了,先上個碼

using System;
using System.Linq;
using System.Dynamic;
using System.Reflection;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Collections.Concurrent;

namespace Tools
{
    public class Class2Map
    {
        private static readonly ConcurrentDictionary<RuntimeTypeHandle, PropertyInfo[]>
           DynamicObjectProperties = new ConcurrentDictionary<RuntimeTypeHandle, PropertyInfo[]>();

        private static PropertyInfo[] GetObjectProperties<T>()
        {
            var type = typeof(T);
            var key = type.TypeHandle;
            PropertyInfo[] queryPts = null;

            DynamicObjectProperties.TryGetValue(key, out queryPts);

            if (queryPts == null)
            {
                queryPts = type.GetProperties();
                DynamicObjectProperties.TryAdd(key, queryPts);
            }

            return queryPts;
        }

        /// <summary>
        /// 單個物件對映
        /// </summary>
        /// <typeparam name="T">型別</typeparam>
        /// <param name="source">例項</param>
        /// <param name="injectAct">map方法集</param>
        /// <returns>對映後的動態物件</returns>
        public static IDictionary<string, Object> DynamicResult<T>(T source, params MapCondition[] injectAct)//where T : ICustomMap
        {
            var queryPts = GetObjectProperties<T>();
            var dynamicResult = new ExpandoObject() as IDictionary<string, Object>;

            foreach (var p in queryPts)
            {
                var attributes = p.GetCustomAttributes(typeof(IngoreProperty), true);
                if (attributes.FirstOrDefault() != null) continue;

                var _name = p.Name;                     //原來是屬性名
                var _value = p.GetValue(source, null);  //原來的屬性值
                object _resultvalue = _value;           //最終的對映值

                if (injectAct != null)
                {
                    string _tempname = null;
                    var condition = injectAct.FirstOrDefault(x => x.Orginal == _name);
                    if (CheckChangeInfo(condition, out _tempname))
                    {
                        _resultvalue = condition.fn.Invoke(_value);
                        dynamicResult.Add(_tempname ?? _name, _resultvalue);
                        continue;
                    }
                }

                //var value = Convert.ChangeType(value,typeof(string));  
                dynamicResult.Add(_name, _resultvalue);
            }

            return dynamicResult;
        }

        /// <summary>
        /// 合併2個物件
        /// </summary>
        /// <typeparam name="TSource">物件1型別</typeparam>
        /// <typeparam name="TTarget">物件2型別</typeparam>
        /// <param name="s">物件1例項</param>
        /// <param name="t">物件2例項</param>
        /// <returns>合併後的動態物件</returns>
        public static IDictionary<string, Object> MergerObject<TSource, TTarget>(TSource s, TTarget t)
        {
            var targetPts = GetObjectProperties<TSource>();

            PropertyInfo[] mergerPts = null;
            var _type = t.GetType();
            mergerPts = _type.Name.Contains("<>") ? _type.GetProperties() : GetObjectProperties<TTarget>();

            var dynamicResult = new ExpandoObject() as IDictionary<string, Object>;

            foreach (var p in targetPts)
            {
                var attributes = p.GetCustomAttributes(typeof(IngoreProperty), true);
                if (attributes.FirstOrDefault() != null) continue;

                dynamicResult.Add(p.Name, p.GetValue(s, null));
            }
            foreach (var p in mergerPts)
            {
                var attributes = p.GetCustomAttributes(typeof(IngoreProperty), true);
                if (attributes.FirstOrDefault() != null) continue;

                dynamicResult.Add(p.Name, p.GetValue(t, null));
            }

            return dynamicResult;
        }
        /// <summary>
        /// 合併2個物件
        /// </summary>
        /// <typeparam name="TSource">物件1型別</typeparam>
        /// <typeparam name="TTarget">物件2型別</typeparam>
        /// <param name="s">物件1例項</param>
        /// <param name="t">物件2例項</param>
        /// <returns>合併後的動態物件</returns>
        public static List<IDictionary<string, Object>> MergerListObject<TSource, TTarget>(List<TSource> s, TTarget t)
        {
            var targetPts = GetObjectProperties<TSource>();

            PropertyInfo[] mergerPts = null;
            var _type = t.GetType();
            mergerPts = _type.Name.Contains("<>") ? _type.GetProperties() : GetObjectProperties<TTarget>();

            var result = new List<IDictionary<string, Object>>();

            s.ForEach(x =>
            {
                var dynamicResult = new ExpandoObject() as IDictionary<string, Object>;

                foreach (var p in targetPts)
                {
                    var attributes = p.GetCustomAttributes(typeof(IngoreProperty), true);
                    if (attributes.FirstOrDefault() != null) continue;

                    dynamicResult.Add(p.Name, p.GetValue(x, null));
                }

                foreach (var p in mergerPts)
                {
                    var attributes = p.GetCustomAttributes(typeof(IngoreProperty), true);
                    if (attributes.FirstOrDefault() != null) continue;

                    dynamicResult.Add(p.Name, p.GetValue(t, null));
                }

                result.Add(dynamicResult);
            });

            return result;
        }

        private static bool CheckChangeInfo(MapCondition condition, out string name)
        {
            name = null;

            bool result = condition != null &&
                          condition.fn != null &&
                          !string.IsNullOrWhiteSpace(condition.Orginal);//&&
                        //!string.IsNullOrWhiteSpace(condition.NewName);

            if (result)
            {
                var temp = condition.NewName;
                name = (string.IsNullOrWhiteSpace(temp) || temp.Trim().Length == 0) ? null : temp;
            }

            return result;
        }
    }
}
測試一下:
List<KeyValue> kk = new List<KeyValue> 
{ 
    new KeyValue{key="aaa", value="111"},
    new KeyValue{key="bbb", value="222"},
    new KeyValue{key="ccc", value="333"},
    new KeyValue{key="ddd", value="444"},
};

var result = Class2Map.MergerListObject<KeyValue, dynamic>(kk, new { p = "jon test" });
var json = JsonUtils.JsonSerializer(result);

Response.Write(json);
輸出如下:
[{"key":"aaa","value":"111","p":"jon test"},{"key":"bbb","value":"222","p":"jon test"},{"key":"ccc","value":"333","p":"jon test"},{"key":"ddd","value":"444","p":"jon test"}]

var result = Class2Map.MergerObject<KeyValue, dynamic>(
                new KeyValue { key = "aaa", value = "111" },
                new { p = "jon test" }
            );
var json = JsonUtils.JsonSerializer(result);

Response.Write(json);
輸出如下:
{ "key": "aaa", "value": "111", "p": "jon test" }