您可以使用反射(C#)来完成以下操作:
public static TOut Map<TIn, TOut>(TIn source)
where TOut : new()
{
var inPropDict = typeof(TIn).GetProperties()
.Where(p => p.CanRead)
.ToDictionary(p => p.Name);
var outProps = typeof(TOut).GetProperties()
.Where(p => p.CanWrite);
var destination = new TOut();
foreach (var outProp in outProps) {
if (inPropDict.TryGetValue(outProp.Name, out var inProp)) {
object sourceValue = inProp.GetValue(source);
if (inProp.PropertyType != outProp.PropertyType) {
sourceValue = Convert.ChangeType(sourceValue, outProp.PropertyType);
}
outProp.SetValue(destination, sourceValue);
}
}
return destination;
}
Reflection使您能够检查类型并获取其属性、字段等信息。
`Type.GetProperties()`返回一个`PropertyInfo`数组,其中包含有关属性的名称、类型和其他信息。它还允许您从对象中读取或写入属性。
上面的代码只是一个快速而简单的示例,没有异常处理。它仅进行浅层映射,并不映射集合或嵌套对象。同时,它也可以通过允许您声明未具有相同名称的属性的映射来进行改进。
有一种工具可以完成所有这些事情及更多,它叫做
AutoMapper。
使用手动映射方法的解决方案
我建议定义一个如下的接口
public interface IMapper<T1, T2>
{
T2 Map(T1 input);
}
具体实现示例:
public class ExampleFromToMapper : IMapper<ExampleFrom, ExampleTo>
{
public ExampleTo Map(ExampleFrom input)
{
return new ExampleTo {
ExampleValue = input.ExampleValue
};
}
}
这个想法是使用依赖注入来选择正确的映射器。
你可以使用NuGet包
Microsoft.Extensions.DependencyInjection作为示例。但是还有许多其他的依赖注入框架存在。
编写一个配置映射器的方法(在此示例中作为扩展方法):
public static IServiceCollection AddMappers(this IServiceCollection services)
{
return services
.AddSingleton<IMapper<ExampleFrom, ExampleTo>, ExampleFromToMapper>()
.AddSingleton<IMapper<OtherFrom, OtherTo>, OtherFromToMapper>();
}
在某处定义容器:
public static class Config
{
public static ServiceProvider Container { get; set; }
}
在应用程序启动时配置容器。
var services = new ServiceCollection();
services
.AddMappers()
.AddTransient<MyForm>();
Config.Container = services.BuildServiceProvider();
作为一个例子,假设您有一个WinForms应用程序,其中一个表单被定义为这样(它直接使用映射器,但也可以使用其他使用映射器的服务。DI容器递归解析依赖项并自动将它们注入构造函数):
public partial class MyForm : Form
{
private readonly IMapper<ExampleFrom, ExampleTo> _mapper;
public MyForm(IMapper<ExampleFrom, ExampleTo> mapper)
{
_mapper = mapper;
InitializeComponent();
}
}
现在,您可以这样启动应用程序:
var frm = Config.Container.GetRequiredService<MyForm>();
Application.Run(frm);
好的,一开始看起来很复杂,但是一旦你建立了基础,添加新服务就变得很容易。每个提供某些功能的类都被视为一个服务。