Как копировать данные из разных классов, сопоставляя имена полей или свойств - PullRequest
3 голосов
/ 13 сентября 2010

Я ищу способ взять два объекта с одинаковыми свойствами и сделать вызов, чтобы скопировать значения свойств из одного объекта в другой. В приведенном ниже примере предполагается, что у меня есть экземпляр A, и я хочу использовать данные этого экземпляра для гидратации нового экземпляра или C (для краткости, я использовал поля вместо свойств в примере ниже)

public class A : B
{
    public string prop1;
    public int prop2;
}

public class B
{
    public byte propX;
    public float propY;
}

public class C
{
    public byte propX;
    public float propY;
    public string prop1;
    public int prop2;
}

public class Merger
{
    public static object Merge(object copyFrom, object copyTo)
    { 
        //do some work
        //maybe <T> generically refactor?
    }
}

Класс слияния - это просто псевдо-пример, выполнение этого с помощью дженериков было бы оптимальным, но первое, что я спрашиваю, - существует ли такая возможность. Я мог вообразить, что использовал рефлексию, чтобы сделать это сам, но сначала хотел отбросить это для лучших идей.

Контекст реального мира. На самом деле это проблема, связанная с MVVM, поскольку я пытаюсь использовать разрозненные классы, возвращающиеся из EF, для заполнения экземпляра ViewModel.

Ответы [ 4 ]

8 голосов
/ 13 сентября 2010

Ознакомьтесь с инструментами и библиотеками, такими как AutoMapper - они бы легко обрабатывали подобные случаи - и многое другое! Не нужно заново изобретать колесо - просто используйте инструмент! : -)

Вы в основном определяете карту между классами A и C следующим образом:

Mapper.CreateMap<A, C>();

и позже вы можете заставить AutoMapper выполнять сопоставление, основанное на этой карте, из экземпляра A в экземпляр C, что-то вроде этого:

C yourC = Mapper.Map<A, C>(instanceOfA);

AutoMapper выполняет сопоставление по умолчанию на основе имен (и типов) свойств, но вы можете расширять и влиять на него множеством способов, чтобы включить сопоставления из одного свойства в другое, даже если имена (или типы) не совпадают 100%. Он достаточно гибкий и хорошо зарекомендовал себя - безусловно, стоит серьезно взглянуть!

1 голос
/ 13 сентября 2010
using System;
using System.Linq;
using System.Reflection;

public class Merger
{
    public static TTarget Merge<TTarget>(object copyFrom) where TTarget : new()
    {
        var flags = BindingFlags.Instance | BindingFlags.Public |
                    BindingFlags.NonPublic;
        var targetDic = typeof(TTarget).GetFields(flags)
                                       .ToDictionary(f => f.Name);
        var ret = new TTarget();
        foreach (var f in copyFrom.GetType().GetFields(flags))
        {
            if (targetDic.ContainsKey(f.Name))
                targetDic[f.Name].SetValue(ret, f.GetValue(copyFrom));
            else
                throw new InvalidOperationException(string.Format(
                    "The field “{0}” has no corresponding field in the type “{1}”.",
                    f.Name, typeof(TTarget).FullName));
        }
        return ret;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new A { prop1 = "one", prop2 = 2, propX = 127, propY = 0.47f };
        var c = Merger.Merge<C>(a);
        Console.WriteLine(c.prop1);  // prints one
        Console.WriteLine(c.prop2);  // prints 2
        Console.WriteLine(c.propX);  // prints 127
        Console.WriteLine(c.propY);  // prints 0.47
    }
}
0 голосов
/ 13 сентября 2010

Хороший пост об использовании AutoMapper для решения этой проблемы в контексте MVVM и MVC

http://www.bengtbe.com/blog/post/2009/04/14/Using-AutoMapper-to-map-view-models-in-ASPNET-MVC.aspx

0 голосов
/ 13 сентября 2010

Это далеко не лучшее решение, но на основе предоставленного вами графа объектов вы, вероятно, могли бы добиться этого путем сериализации XML первого объекта и десериализации XML потока XML во второй объект.

Ваш предложенный метод Merger может выглядеть следующим образом:

public class Merger
{
    public static object Merge(object copyFrom, object copyTo)
    { 
        var xmlContent = MyXMLSerializationMethod(copyFrom);
        MyXMLDeserializationMethod(xmlContent, typeof(copyTo), out copyTo);
        return copyTo;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...