使用反射来映射相似的对象:对象与目标类型不匹配

3

尽管我看了多个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}

1个回答

5
您正在使用的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实际上与对象有任何有意义的连接。我只是把它看作是关于属性的一个脱离实际对象的事实表,只要对象之间的名称和类型相同,为什么它们在技术上是两个不同的类就无所谓呢?但显然你不能把它们分开。知道这点很好! - Ari Roth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接