“Deep"转换对象

本文关键字:转换 对象 quot Deep | 更新日期: 2023-09-27 18:15:21

我有以下方法可以用来将对象转换为给定的类型:

public static TTarget Convert<TTarget>(object source) where TTarget : new()
{
    var target = new TTarget();
    Type targetType = typeof (TTarget);
    foreach (PropertyInfo sourceProperty in source.GetType().GetProperties())
    {
        if (!sourceProperty.CanRead || (sourceProperty.GetIndexParameters().Length > 0))
            continue;
        PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);
        if ((targetProperty != null) && (targetProperty.CanWrite))
            targetProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
    }
    return target;
}

对于具有值类型等属性的简单类,它可以很好地工作,但是需要映射到另一个类的复杂属性,它不是很清楚如何去做。如果我将映射存储到一个静态属性中:

private static Dictionary<Type, Type> Mappings;
static TypeConverter()
{
    Mappings = new Dictionary<Type, Type>
        {
            {typeof (DbSpace), typeof (DmsSpace)},
            {typeof (DbDirectory), typeof (DmsDirectory)},
            {typeof (DbFile), typeof (DmsFile)}
        };
}

我似乎找不到一种方法来找到一种方法来利用这些信息来转换复杂的属性。如何使用上面的映射来转换复杂的属性?

问题的关键是:如果我只有一个Type对象,我如何调用new ?

“Deep"转换对象

Activator.CreateInstance(type),这里有一个msdn的链接给那些认为我的回答不够"详细"的人(得到3个反对,因为这个答案是必要的简短)…

你也看过AutoMapper吗?

您可以使用许多序列化器 (JavaScriptSerializer, XmlSerializer, Json。净等等。)只要属性名匹配,就可以对对象进行"深度转换"。

我将给出一个使用JavaScriptSerializer的例子

var class1 = new Class1() { Property = "a", SubProperty = new SubClass1() { SubProperty = "b" } };
var class2 = Convert<Class2>(class1);

public static TTarget Convert<TTarget>(object source) where TTarget : new()
{
    var ser = new JavaScriptSerializer();
    var json = ser.Serialize(source);
    return ser.Deserialize<TTarget>(json);
}

.

public class Class1
{
    public string Property { get; set; }
    public SubClass1 SubProperty { get; set; }
}
public class SubClass1
{
    public string SubProperty { get; set; }
}

public class Class2
{
    public string Property { get; set; }
    public SubClass2 SubProperty { get; set; }
}
public class SubClass2
{
    public string SubProperty { get; set; }
}

使用AutoMapper代替手动操作

使用Activator.CreateInstance解决方案:

public static class TypeConverter
{
    private static Dictionary<Type, Type> Mappings;
    static TypeConverter()
    {
        Mappings = new Dictionary<Type, Type>
            {
                {typeof (DbSpace), typeof (DmsSpace)},
                {typeof (DbDirectory), typeof (DmsDirectory)},
                {typeof (DbFile), typeof (DmsFile)}
            };
    }
    public static object Convert(object source, Type targetType)
    {
        var target = Activator.CreateInstance(targetType);
        foreach (PropertyInfo sourceProperty in source.GetType().GetProperties())
        {
            if (!sourceProperty.CanRead || (sourceProperty.GetIndexParameters().Length > 0))
                continue;
            PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);
            object value = sourceProperty.GetValue(source, null);
            if ((targetProperty != null) && (targetProperty.CanWrite))
            {
                if (value != null)
                {
                    Type valueType = value.GetType();
                    Type mappedTypeKey = Mappings.Keys.FirstOrDefault(valueType.IsAssignableFrom);
                    if (mappedTypeKey != null)
                    {
                        targetProperty.SetValue(target, Convert(value, Mappings[mappedTypeKey]), null);
                    }
                    else
                    {
                        targetProperty.SetValue(target, value, null);
                    }
                }
                else
                {
                    targetProperty.SetValue(target, null, null);
                }
            }
        }
        return target;
    }
    public static TTarget Convert<TTarget>(object source) where TTarget : class, new()
    {
        return Convert(source, typeof (TTarget)) as TTarget;
    }
}
使用TypeConverter类的示例代码:
spaces = ctx.DbSpaces.Include("Root").ToList().Select(TypeConverter.Convert<DmsSpace>).ToList();

使用AutoMapper解决方案:

Mapper.CreateMap<DbSpace, DmsSpace>();
Mapper.CreateMap<DbSpace, IDmsSpace>();
Mapper.CreateMap<DbDirectory, DmsDirectory>();
Mapper.CreateMap<DbDirectory, IDmsDirectory>();

使用AutoMapper的示例代码:

spaces = ctx.DbSpaces.Include("Root").ToList().Select(Mapper.Map<DmsSpace>).ToList();