C# 使用反射映射类似对象:对象与目标类型不匹配

C# 使用反射映射类似对象:对象与目标类型不匹配,c#,reflection,mapping,C#,Reflection,Mapping,我在这里完全不知所措,尽管我看了很多SO帖子和其他我能想到的东西 我的目标是制作一个非常非常简单的映射器。我基本上可以在一些单元测试中用作工具。它不需要复杂或任何东西——只需将一个对象的高级原语和字符串值映射到另一个对象。因此,基本算法是: 从t从t获取所有属性 从TTo 获取两者中按名称匹配的所有属性。 我知道这可能是一个错误,因为它们可能有相同的名称,但有不同的类型,但让我们把它放在一边。这不是我在这里遇到的问题——类之间的属性和类型匹配 创建可复制到的TTo实例 对于在对象之间映射的每

我在这里完全不知所措,尽管我看了很多SO帖子和其他我能想到的东西

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

  • t从
    t获取所有属性
  • TTo
  • 获取两者中按名称匹配的所有属性。
    • 我知道这可能是一个错误,因为它们可能有相同的名称,但有不同的类型,但让我们把它放在一边。这不是我在这里遇到的问题——类之间的属性和类型匹配
  • 创建可复制到的
    TTo
    实例
  • 对于在对象之间映射的每个特性:
  • 对象中获取
    的值
  • 将值转换为属性的类型
  • 对象上的值设置为
  • 问题是,无论我做什么,也不管属性的类型是什么(
    int
    string
    ),我都会得到以下结果:

    var propTo = typeof(B).GetProperty(prop.Name);
    propTo.SetValue(b, converted, null);
    Console.WriteLine(b.Id); // Output: Test
    
    对象与目标类型不匹配

    以下是我正在使用的代码:

    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();
        }
    }
    
    公共TTo映射(来自)
    {
    if(from==null)返回默认值;
    var fromProps=GetProperties(typeof(TFrom));
    var toProps=GetProperties(typeof(TTo));
    //可以从一个映射到另一个的道具
    var propsToCopy=fromProps.Intersect(toProps,newpropertycomparer()).ToList();
    var returnObject=(TTo)Activator.CreateInstance(typeof(TTo));
    foreach(propsToCopy中的var prop)
    {
    //复制值
    var fromValue=prop.GetValue(from,null);
    var convertedValue=Convert.ChangeType(fromValue,prop.PropertyType);
    prop.SetValue(returnObject,convertedValue,null);
    }
    返回对象;
    }
    公共属性信息[]获取属性(类型objectType)
    {
    var allProps=objectType.GetProperties(
    BindingFlags.Public | BindingFlags.Instance);
    返回allProps.Where(p=>p.PropertyType.IsPrimitive||
    p、 PropertyType==typeof(string)).ToArray();
    }
    私有类属性比较程序:IEqualityComparer
    {
    公共布尔等于(PropertyInfo x,PropertyInfo y)
    {
    返回x.Name.Equals(y.Name);
    }
    public int GetHashCode(PropertyInfo obj)
    {
    返回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);
    
    公共类Foo
    {
    公共字符串StringProp{get;set;}
    公共int IntProp{get;set;}
    }
    公共类Foother
    {
    公共字符串StringProp{get;set;}
    公共int IntProp{get;set;}
    }
    var foo=new foo{IntProp=1,StringProp=“foo”};
    var mappedFoo=Map(foo);
    

    关于我从VisualStudio中得到的唯一提示是从watch窗口:如果属性类型是
    字符串
    ,watch窗口将
    convertedValue
    的类型报告为
    对象
    。如果属性类型是
    int
    ,则监视窗口会报告
    对象{int}

    您正在使用的属性信息仍然与它所表示的属性所属的类型相耦合,因此您无法使用它来设置另一类型对象的值,而不会出现错误

    下面是一个简短的行为示例:

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

    成功了,谢谢!我想我的问题是我没有想到
    属性info
    实际上以任何有意义的方式连接到对象。我只是把它看作是一个与属性无关的事实说明——只要对象之间的名称和类型相同,那么从技术上讲它们是两个独立的类又有什么关系呢?但显然,你不能断开它们。很高兴知道!