“深度”转换对象

时间:2013-05-26 21:34:59

标签: c#

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

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)}
        };
}

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

问题的症结在于:如果我只有一个new对象,如何调用Type

4 个答案:

答案 0 :(得分:5)

Activator.CreateInstance(type),这里有msdn的链接,因为那些认为我的答案不够“精心”的人(获得3个downvotes for spot-on as-short-as-essential答案)..

您是否也查看了AutoMapper

答案 1 :(得分:2)

您可以使用许多序列化程序(JavaScriptSerializer,XmlSerializer,Json.Net等)  只要符号名称匹配,就可以对对象进行“深度转换”。

我将使用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; }
}

答案 2 :(得分:1)

使用AutoMapper而不是手动执行。

答案 3 :(得分:0)

使用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();