(一)前言
繼《對象屬性之間的相互賦值 》後,關於集合對象屬性的賦值,主要可以通過循環遍歷集合中的對象來進行屬性間的賦值。這些可以運用於不同對象之間、相關屬性類似的情況。最常見的是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/