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