AutoCopy 一个可以缩短开发时间的工具类,帮助程序员从某些繁重的人肉编码中解脱出来。灵感来自于**AutoMapper**,初次使用AutoMapper时被它实现的功能深深吸引,但是在逐渐学习中发现AutoMapper速度不能令人满意,并且实现有些复杂,小白不容易看懂原理,所以萌生了自己写一个更简单、更高效类库的想法。在这样一个契机之下,AutoCopy应运而生(:clap::smirk:)。AutoCopy的一些方法模仿了AutoMapper的命名和使用方式,降低使用上的难度。
- 执行速度快
- 基于抽象类TargetExpressionProviderBase可以实现任意扩展
- 支持自动/手工的类型转换
- 支持多AutoCopy实例嵌套
执行次数:100,000
| 操作 | 平均时间(毫秒) |
|---|---|
| 手动映射 | 4.267375 |
| AutoCopy | 4.18163333333333 |
| AutoMapper | 42.4985 |
执行次数:1,000,000
| 操作 | 平均时间(毫秒) |
|---|---|
| 手动映射 | 30.884225 |
| AutoCopy | 38.647675 |
| AutoMapper | 322.8877 |
执行次数:10,000,000
| 操作 | 平均时间(毫秒) |
|---|---|
| 手动映射 | 440.14825 |
| AutoCopy | 459.17575 |
| AutoMapper | 3895.974725 |
Benchmark code see here
在调用Register方法时基于Reflection分析源类和目标类的所有属性,并生成Expression列表,之后使用Expression.Lambda方法把Expression列表以及相应参数包装成LambdaExpression,通过调用Compile方法编译为Func Delegate。
public class Address
{
public string ZipCode { get; set; }
}
var autoCopy = AutoCopy.CreateMap<Address, Address>();
autoCopy.Register();
Address a=new Address { ZipCode="1234567890"; };
Address b=autoCopy.Map(a); public class Address
{
public string ZipCode { get; set; }
}
public class Telephone
{
public string Number { get; set; }
}
public class Customer
{
public Address Address { get; set; }
public Telephone Phone { get; set; }
public string Memo { get; set; }
}
public class CustomerInfo
{
public string zipCode { get; set; }
public string PhoneNumber { get; set; }
public string Memo { get; set; }
}
var autoCopy = AutoCopy.CreateMap<Customer, CustomerInfo>();
autoCopy
.ForMember(p => p.zipCode, opt => opt.MapFrom(p => p.Address.ZipCode))
.ForMember(p => p.PhoneNumber, opt => opt.MapFrom(p => p.Phone.Number));
autoCopy.Register();
Customer customer = new Customer();
customer.Address = new Address { ZipCode = "1234567890" };
customer.Phone = new Telephone { Number = "17791704580" };
customer.Memo = "Test";
CustomerInfo info = autoCopy.Map(customer); public class Data
{
public int width { get; set; }
public int height { get; set; }
public string ua { get; set; }
public string ip { get; set; }
public string imei { get; set; }
public string android_id { get; set; }
public string make { get; set; }
public string model { get; set; }
public string os { get; set; }
public string osv { get; set; }
public int connectionType { get; set; }
public int deviceType { get; set; }
public string mac { get; set; }
public int screenWidth { get; set; }
public int screenHeight { get; set; }
public string appName { get; set; }
public int ppi { get; set; }
public string dpidsha1 { get; set; }
public string plmn { get; set; }
public string orientation { get; set; }
public int pos { get; set; }
public bool instl { get; set; }
public string ver { get; set; }
public string bundle { get; set; }
public Ext ext { get; set; }
}
public class Ext
{
public int ID { get; set; }
}
string surl = "id=10010&width=10&height=10&ua=ua&ip=127.0.0.1&imei=00000000000000&android_id=A00000000000000&make=1111111111&model=XXX&os=android&osv=4.0.1&connectionType=1&deviceType=1&mac=0.0.0.0.0.0.0&screenWidth=100&screenHeight=100&appName=test&ppi=600&dpidsha1=dpidsha1&plmn=1&orientation=1&pos=1&instl=true&ver=1.0.0&bundle=bundle";
HttpQueryCollection collection = new HttpQueryCollection(surl, false);
var ac = AutoCopy.CreateMap<NameValueCollection, Ext>();
ac.Provider= new HttpRequestParamsExpressionProvider(typeof(NameValueCollection));
var autoCopy = AutoCopy.CreateMap<NameValueCollection, Data>();
autoCopy.ForMember(p => p.ext, opt => opt.MapFrom(p=>ac.Map(p)));
autoCopy.Provider = new HttpRequestParamsExpressionProvider(typeof(NameValueCollection));
autoCopy.Register();
Data data=autoCopy.Map(collection);内部类TypeConverter的TryConvert方法通过以下顺序进行类型的自动转换:
- 是否存在显式转换操作符
- 是否存在隐式转换操作符
- 是否存在继承关系
- 是否存在Convert.ToXXX方法
- 是否可以调用目标类型上的TryParse方法
- 调用Convert.ChangeType方法
通过调用**AutoCopy<T, D>类实例的ForTypeConvert<T1, T2>**方法来注册类型转换。
抽象类TargetExpressionProviderBase的TryGetExpression方法参数说明
假定AutoCopy<T1, T2>中T1为源类型,T2为目标类型
| 属性名 | 备注 | |
|---|---|---|
| 1 | name | 目标属性名称 |
| 2 | parameter | 源类型的参数表达式 |
| 3 | destType | 目标类型 |
| 4 | exp | 通过TryGetExpression方法最后生成的表达式 |
| 5 | variable | 临时变量 |
| 6 | test | 测试表达式 |
| 7 | ifTrue | 是否需要测试;如果该值为true,则只有test执行返回true时才会继续执行exp |
2017-12-05 增加DataRow映射到实体类的示例程序
2017-12-13 调整了AutoCopy<,>类的参数顺序并且修改了Option.ResolveUsing方法参数类型错误的bug
2017-12-26 增加CopyMapAttribute特性来支持目标类型属性的别名映射
2017-12-27 增加CopyRequiredAttribute特性来支持检测目标类型属性所需要映射的值是否为空 [需要进一步测试]
2017-12-28 当Option.MapFrom函数中调用其他AutoCopy实例时从Decompiler函数中获取新的LambdaExpression
2018-01-12 重载Option.MapForm函数,添加一个字符串参数,指示目标的属性名称或映射名称
2018-02-09 修复bug:当将CopyRequired特性应用于需要AutoCopy实例嵌套映射的属性时,调用ConditionFalseRewriter类的Visit函数将导致错误
由于AutoCopy在运行时通过主动调用Register方法使用反射分析类的属性,所以如果方法进行了混淆可能会出现Bug。