(一)前言
繼《對象屬性之間的相互賦值 》後,關於集合對象屬性的賦值,主要可以通過循環遍歷集合中的對象來進行屬性間的賦值。這些可以運用於不同對象之間、相關屬性類似的情況。最常見的是web services與silverlight之間的對象賦值(對象之間的屬性值只有一部分是需要的),這樣可以減少silverlight對web services的依賴。
(二)具體實現
通過反射將源對象與目標對象之間的屬性賦值。源對象的屬性名、屬性類型必須與目標對象的屬性名、屬性類型一致,並且源對象的屬性必須是可讀的,目標對象的屬性是可寫的(僅針對於需要賦值的屬性來說)。具體的源代碼如下:
public class ObjectMapper { private static readonly Dictionary<string, IList<PropertyMapper>> _mappers = new Dictionary<string, IList<PropertyMapper>>(); public static IList<PropertyMapper> GetMapperProperties( Type sourceType, Type targetType) { var sourceProperties = sourceType.GetProperties(); var targetProperties = targetType.GetProperties(); return (from s in sourceProperties from t in targetProperties where s.Name == t.Name && s.CanRead && t.CanWrite && s.PropertyType == t.PropertyType select new PropertyMapper { SourceProperty = s, TargetProperty = t }).ToList(); } public static void CopyProperties<T1, T2>(List<T1> sources, List<T2> targets) where T2:new() { if (sources == null || sources.Count == 0 || targets == null) { return; } T2 target; foreach (T1 source in sources) { target = new T2(); CopyProperties(source, target); targets.Add(target); } } public static void CopyProperties(object source, object target) { if (source == null || target == null) { return; } var sourceType = source.GetType(); var targetType = target.GetType(); string key = GetMapperKey(sourceType, targetType); if (!_mappers.ContainsKey(key)) { MapperProperties(sourceType, targetType); } var mapperProperties = _mappers[key]; SetPropertityValue(source, target, mapperProperties); } private static void SetPropertityValue(object source, object target, IList<PropertyMapper> mapperProperties) { for (int index = 0, count = mapperProperties.Count; index < count; index++) { var property = mapperProperties[index]; var sourceValue = property.SourceProperty.GetValue(source, null); property.TargetProperty.SetValue(target, sourceValue, null); } } protected static string GetMapperKey(Type sourceType, Type targetType) { return string.Format("{0}_{1}", sourceType.FullName, targetType.FullName); } public static void MapperProperties(Type source, Type target) { if (source == null || target == null) { return; } string key = GetMapperKey(source, target); if (_mappers.ContainsKey(key)) { return; } var properties = GetMapperProperties(source, target); _mappers.Add(key, properties); } }
有效的方法主要為如下2個:
(1)第22-36行CopyProperties<T1, T2>(List<T1> sources, List<T2> targets) where T2:new()
目標對象集合必須添加約束(where T2:new())---必須有默認構造函數。從如下的代碼可以看出 target = new T2()必須通過實例化,才能將其添加到目標集合中。
T2 target;
30 foreach (T1 source in sources)
31 {
32 target = new T2();
33 CopyProperties(source, target);
34 targets.Add(target);
35 }
(2)CopyProperties(object source, object target)
這個方法才是最關鍵的,因為集合對象之間的賦值主要是通過循環該方法來賦值的。
(三)單元測試
對象集合對象之間的賦值,相關的單元測試代碼如下(僅考慮了簡單的對象),通過下面的測試,可以檢測到集合對象屬性賦值賦值後,值是相同的:
[TestMethod()] public void CopyPropertiesTest1() { List<Jasen.Core.Info> sources = new List<Core.Info>(); for (int index = 0; index < 10; index++) { sources.Add(new Jasen.Core.Info() { Name = "jasen", CreateTime = "2011-5-13".AsDateTime(), Exist = true, ConflictOption = ConflictOption.OverwriteChanges, Index = index }); } List<Info> targets = new List<Info>(); ObjectMapper.CopyProperties(sources, targets); for (int index = 0, count = sources.Count; index < count; index++) { Assert.AreEqual(sources[index].ConflictOption, targets[index].ConflictOption); Assert.AreEqual(sources[index].CreateTime, targets[index].CreateTime); Assert.AreEqual(sources[index].Exist, targets[index].Exist); Assert.AreEqual(sources[index].Name, targets[index].Name); //Assert.AreEqual(sources[index].Index, targets[index].Index); } }
(四)總結
以上的代碼僅僅針對於簡單的屬性之間的賦值,對於對象包含IList集合屬性的賦值,可以將該IList再進行一次賦值即可。上文中采用字典來使性能提高,當第一次賦值的時候保存需要賦值的相關聯的屬性對集合。第二次的時候就直接通過鍵值獲取該屬性對,不需要再次查找相關的屬性對集合。
源代碼下載:Jasen.CopyObjectToObjectSample.rar
http://files.cnblogs.com/jasenkin/Jasen.CopyObjectToObjectSample.rar
作者:JasenKin
出處:http://www.cnblogs.com/jasenkin/