【问题标题】:Mapping similar objects using reflection: Object does not match target type使用反射映射相似对象:对象与目标类型不匹配
【发布时间】:2018-10-28 09:19:37
【问题描述】:

尽管查看了多个 SO 帖子和我能想到的任何其他内容,但我在这里完全不知所措。

我的目标是制作一个非常非常简单的映射器。我基本上可以在某些单元测试中用作工具。它不需要复杂或任何东西——只需将一个对象的高级原始值和字符串值映射到另一个对象。所以基本算法是:

  1. TFrom获取所有属性
  2. TTo获取所有属性
  3. 获取两者中的所有属性,按名称匹配。
    • 我知道这可能是一个错误,因为它们可能具有相同的名称但类型不同,但让我们把它放在一边。这不是我在这里遇到的 - 类之间的属性和类型匹配。
  4. 创建一个我们可以复制到的TTo 实例。
  5. 对于在对象之间映射的每个属性:
    1. from 对象中获取值
    2. 将值转换为属性的类型
    3. 设置to对象的值

问题是,无论我做什么,无论属性是什么类型(例如intstring),我都会得到以下信息:

对象与目标类型不匹配。

这是我正在使用的代码:

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}

【问题讨论】:

    标签: c# reflection mapping


    【解决方案1】:

    您正在使用的 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);
    }
    

    【讨论】:

    • 成功了,谢谢!我认为我的挂断是我不认为PropertyInfo 实际上以任何有意义的方式连接到对象。我只是认为它类似于关于属性的不连贯的情况说明书——只要对象之间的名称和类型相同,为什么它们在技术上是两个独立的类有什么关系呢?但显然,你不能断开它们。很高兴知道!
    猜你喜欢
    • 2013-10-06
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 1970-01-01
    • 2019-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多