Сопоставление похожих объектов с помощью отражения: объект не соответствует типу цели - PullRequest
0 голосов
/ 18 мая 2018

Я в полном недоумении, несмотря на просмотр нескольких SO-сообщений и всего, что я могу придумать.

Моя цель здесь - создать действительно очень простой картограф.Что-то, что я могу использовать в качестве инструмента в некоторых юнит-тестах.Он не должен быть сложным или чем-то еще - просто сопоставьте высокоуровневые примитивы и строковые значения одного объекта другому.Итак, основной алгоритм:

  1. Получить все свойства из TFrom
  2. Получить все свойства из TTo
  3. Получить все свойства, которые в обоих совпадаютпо имени.
    • Я знаю, что это может быть ошибкой в ​​том, что они могут иметь одно и то же имя, но другого типа, но давайте отложим это.Это не то, с чем я здесь сталкиваюсь - свойства и типы совпадают между классами.
  4. Создайте экземпляр TTo, который мы можем скопировать.
  5. Для каждого свойства, сопоставленного между объектами:
    1. Получить значение из from объекта
    2. Преобразовать значение в тип свойства
    3. Установить значениена объекте to

Проблема в том, что независимо от того, что я делаю, и независимо от того, какой тип свойства (int или string,например) Я получаю следующее:

Объект не соответствует типу цели.

Вот код, который я использую:

public TTo Map<TFrom, TTo>(TFrom from)
{
    if (from == null) return default;

    var fromProps = GetProperties(typeof(TFrom));
    var toProps = GetProperties(typeof(TTo));

    // Props that can be mapped from one to the other
    var propsToCopy = fromProps.Intersect(toProps, new PropertyComparer()).ToList();

    var returnObject = (TTo)Activator.CreateInstance(typeof(TTo));

    foreach (var prop in propsToCopy)
    {
        // Copy the values
        var fromValue = prop.GetValue(from, null);
        var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
        prop.SetValue(returnObject, convertedValue, null);
    }

    return returnObject;
}

public PropertyInfo[] GetProperties(Type objectType)
{
    var allProps = objectType.GetProperties(
        BindingFlags.Public | BindingFlags.Instance);

    return allProps.Where(p => p.PropertyType.IsPrimitive ||
        p.PropertyType == typeof(string)).ToArray();
}

private class PropertyComparer : IEqualityComparer<PropertyInfo>
{
    public bool Equals(PropertyInfo x, PropertyInfo y)
    {
        return x.Name.Equals(y.Name);
    }

    public int GetHashCode(PropertyInfo obj)
    {
        return obj.Name.GetHashCode();
    }
}

И вот пример того, как я бы назвал это, с примерами классов:

public class Foo 
{
    public string StringProp { get; set; }
    public int IntProp { get; set; }
}

public class FooOther
{
    public string StringProp { get; set; }
    public int IntProp { get; set; }
}

var foo = new Foo { IntProp = 1, StringProp = "foo" };
var mappedFoo = Map<Foo, FooOther>(foo);

Об одном подсказке, которую я получил от Visual Studio, - от окна наблюдения: если тип свойстваstring, окно просмотра сообщает тип convertedValue как object.Если тип свойства - int, в окне просмотра появится сообщение object {int}.

1 Ответ

0 голосов
/ 18 мая 2018

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

Вот сокращенный пример поведения:

public class A {
    public string Id {get;set;}
}
public class B {
    public string Id {get;set;}
}

void Main()
{
    var test = new A() { Id = "Test"};
    var prop = test.GetType().GetProperty("Id");

    var b = (B)Activator.CreateInstance(typeof(B));

    var fromValue = prop.GetValue(test);
    var converted = Convert.ChangeType(fromValue, prop.PropertyType);
    prop.SetValue(b, converted, null); // Exception
}

Это имеет смысл, если вы думаете о PropertyInfo как о члене A. Чтобы это исправить, вы захотите получить информацию о свойстве, характерную для вашего типа. Я могу исправить свой пример следующим:

var propTo = typeof(B).GetProperty(prop.Name);
propTo.SetValue(b, converted, null);
Console.WriteLine(b.Id); // Output: Test

Если вы измените содержание вашего foreach на следующее, вы должны быть в открытом виде:

foreach (var prop in propsToCopy)
{
    // Copy the values
    var fromValue = prop.GetValue(from, null);
    var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
    var propTo = typeof(TTO).GetProperty(prop.Name);
    propTo.SetValue(returnObject, convertedValue, null);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...