Github 開源:高效好用的對象間屬性拷貝工具:升訊威 Mapper( Sheng.Mapper)


Github 地址https://github.com/iccb1013/Sheng.Mapper

 

對象屬性值映射/拷貝工具。不需要創建映射規則,不要求對象類型一致,適用於簡單直接的拷貝操作,可以全屬性拷貝,指定屬性拷貝,排除指定的屬性。拷貝包含 10 個屬性的對象 10 萬次,耗時 4.x 秒(普通開發機)。

 

 

+ 拷貝行為只針對 sourceObject 和 targetObject 所共有的屬性
+ 在 sourceObject 和 targetObject 中的待拷貝的屬性值的類型處理:如果是值類型,直接拷貝,如果是引用類型,sourceObject 中的屬性的類型 必須 和 targetObject 中的屬性的類型一致,或是它的派生類
+ 如果要支持類型不一致的屬性自動進行類型轉換,你可以在 PropertyMappingDescription 這個類中實現轉換器功能
+ 拷貝行為 不會 改變 targetObject 中不需要被拷貝的屬性的值
+ 你可以組合使用幾個方法來從多個對象中拷貝指定的屬性值到一個 targetObject

和 AutoMapper 互補,與之相比最大優勢是短,平,快。不需要創建復雜的映射規則,並支持屬性排除操作

 

具體實現:

這里在具體實現上,其實並不復雜,只需對反射操作稍有了解即可,

我們通過 sourceObject 和 targetObject ,獲取它們的“類型(Type)”,然后使用 Type.GetProperties() 方法,獲取這個對象類型所包含的屬性(Property)。

 PropertyInfo[] propertyList = Type.GetProperties(); foreach (PropertyInfo property in propertyList) { PropertyMappingDescription propertyMappingDescription = new PropertyMappingDescription(property); _propertyList.Add(propertyMappingDescription); _propertyNames.Add(property.Name, propertyMappingDescription); }

這里有另外一個細節需要留意的是,我們要把同樣類型(Type)的相關信息,緩存起來,這樣下次再拷貝相同類型的對象時,就無需再去反射它的 Properties。

我們通過 TypeMappingDescription 對對象的類型信息進行緩存和包裝,提供我們所需要的基本操作:

public bool ContainsProperty(string name) { if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("TypeMappingDescription.ContainsProperty 必須指定屬性名。"); return _propertyNames.ContainsKey(name); } public object GetValue(object obj, string propertyName) { if (obj == null) throw new ArgumentNullException("指定的對象為空。"); if (obj.GetType() != this.Type) throw new ArgumentException("指定的對象類型與緩存的對象類型不一致。"); if (_propertyNames.ContainsKey(propertyName) == false) throw new ArgumentOutOfRangeException("指定的屬性名不存在。"); PropertyMappingDescription propertyMappingDescription = (PropertyMappingDescription)_propertyNames[propertyName]; if (propertyMappingDescription.CanRead == false) throw new InvalidOperationException("屬性 " + propertyName + "不可讀。"); return propertyMappingDescription.GetValue(obj); } public void SetValue(object obj, string propertyName, object value) { if (obj == null) throw new ArgumentNullException("指定的對象為空。"); if (obj.GetType() != this.Type) throw new ArgumentException("指定的對象類型與緩存的對象類型不一致。"); if (_propertyNames.ContainsKey(propertyName) == false) throw new ArgumentOutOfRangeException("指定的屬性名不存在。"); PropertyMappingDescription propertyMappingDescription = (PropertyMappingDescription)_propertyNames[propertyName]; if (propertyMappingDescription.CanWrite == false) throw new InvalidOperationException("屬性 " + propertyName + "只讀。"); Type propertyType = propertyMappingDescription.PropertyInfo.PropertyType; if (propertyType.IsValueType == false && value != null) { Type valueType = value.GetType(); if(propertyType != valueType && valueType.IsSubclassOf(propertyType) == false) { throw new ArgumentException("目標對象的 " + propertyName + "與 value 的類型既不一致,也不是目標類型的派生類。"); } } propertyMappingDescription.SetValue(obj, value); }

 

同時我們使用 PropertyMappingDescription 對 PropertyInfo 進行封裝。對 PropertyInfo 進行封裝,是為了方便我們后續針對屬性添加屬性值的轉換器,以便實現稍復雜一些的屬性拷貝操作。

 

最后我們來測試一下拷貝操作:

 A a = new A() { Name = "張三", Age = 10, Class = "一班", CObject = new SubC() { Message = "Hello" }, P1 = "1", P2 = "2", P3 = "3", P4 = "4", P5 = "5", P6 = "6" }; B b = new B(); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < 100000; i++) { //全部屬性拷貝
 ShengMapper.SetValues(a, b); //拷貝指定的屬性 // ShengMapper.SetValuesWithProperties(a, b, new string[] { "Name", "Age", "P1" }); //排除指定的屬性 //ShengMapper.SetValuesWithoutProperties(a, b, new string[] { "Name", "Age", "P1" });
 } stopwatch.Stop(); Console.WriteLine("對包含 10 個屬性的對象的屬性值拷貝 10 萬次,耗時:" + stopwatch.Elapsed.ToString()); Console.ReadLine();

 

我模擬了一幾個類,他們有不同類型的屬性,還包括引用類型的屬性我派生類。

對於包含 10 個屬性的類的 10 萬次屬性值拷貝,在開發機上大約用了 4.x 秒。

 

完整的代碼位於 Github。

https://github.com/iccb1013/Sheng.Mapper

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM