【问题标题】:How to map properties of two different objects?如何映射两个不同对象的属性?
【发布时间】:2013-07-16 11:32:58
【问题描述】:

我想知道如何映射两个不同对象的字段并为其分配值。

示例:

public class employee
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class manager
{
    public int MgrId { get; set; }
    public string MgrName { get; set; }
}

现在我有一个 List 对象。我想将值分配给“经理”类。任何自动的方式来做到这一点。我可以明确地做到这一点并为其赋值。但是我的对象非常大,这就是问题所在。我也不想使用任何第三方工具。

注意:经理不能有前缀。它可以是任何东西。 (例如:mgrId 可以像 mgrCode)

【问题讨论】:

  • “我也不想使用任何第三方工具。” 为什么不呢?
  • 只是一个建议:automapper.org,它是转换此类对象的好工具。
  • 您使用什么来源为经理类赋值?
  • (...)mgrId 可以类似于 mgrCode(...),在某些时候,您需要提供某种映射...跨度>

标签: c# oop object


【解决方案1】:

您可以使用反射,即使忽略属性大小写(注意employee.IDmanager.MgrId):

class Program
{
    static void Main(string[] args)
    {
        var employee = new Employee() { ID = 1, Name = "John" };
        var manager = new Manager();
        foreach (PropertyInfo propertyInfo in typeof(Employee).GetProperties())
        {
            typeof(Manager)
                .GetProperty("Mgr" + propertyInfo.Name,
                    BindingFlags.IgnoreCase |
                    BindingFlags.Instance |
                    BindingFlags.Public)
                .SetValue(manager,
                    propertyInfo.GetValue(employee));
        }
    }
}

public class Employee
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class Manager
{
    public int MgrId { get; set; }
    public string MgrName { get; set; }
}

如果你不知道Mgr前缀,你只能通过后缀匹配:

foreach (PropertyInfo propertyInfo in typeof(Employee).GetProperties())
{
    typeof(Manager).GetMembers()
        .OfType<PropertyInfo>()
        .FirstOrDefault(p => p.Name.EndsWith(propertyInfo.Name, 
            StringComparison.CurrentCultureIgnoreCase))
        .SetValue(manager,
            propertyInfo.GetValue(employee));
}

并且一个非常狭窄且不切实际的假设:基于属性顺序的映射(如果您希望这两种类型具有以相同的顺序和编号定义的属性,唯一的区别是属性名称)。我不建议任何人在现实生活中使用它,但是,它仍然存在(只是为了让它更脆弱 :)):

typeof(Employee)
    .GetProperties()
    .Select((p, index) =>
        new { Index = index, PropertyInfo = p })
    .ToList()
    .ForEach(p =>
        {
            typeof(Manager)
                .GetProperties()
                .Skip(p.Index)
                .FirstOrDefault()
                .SetValue(manager,
                    p.PropertyInfo.GetValue(employee));
        });

【讨论】:

  • PropertyInfo.GetValue 的重载采用源对象。还有一个 PropertyInfo.SetValue 的两个参数重载。
  • 正确,从 4.5 开始。我正在用 3.5 编译 :) 我会更新我的答案。
  • 我想我最近一直在使用 .Net 4.5。
  • @AlexFilipovici 属性必须是相同的类型,还是有办法转换它们?
【解决方案2】:

使用反射或AutoMapper。我推荐后者,因为如果没有目的,编写新代码是一种浪费。

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Manager
{
    public int MgrId { get; set; }
    public string MgrName { get; set; }
}

Mapper.Initialize(cfg =>
{
   cfg.RecognizeDestinationPrefixes("Mgr");
   cfg.CreateMap<Employee, Manager>();
});

var manager = Mapper.Map<Employee, Manager>(new Employee { Id = 1, Name = "Fred" });

Console.WriteLine("Id: {0}", manager.MgrId);
Console.WriteLine("Name: {0}", manager.MgrName);

如果属性没有惯用的源标识符,则使用 AutoMapper 的 projection

Mapper.CreateMap<Employee, Manager>()
      .ForMember(dest => dest.MgrCode, opt => opt.MapFrom(src => src.ID))
      .ForMember(dest => dest.MgrName, opt => opt.MapFrom(src => src.Name))

【讨论】:

  • 是否忽略了属性大小写?
  • 是的,它会忽略属性大小写。
  • 但是这里我需要使用自动映射器。有没有类似序列化的方法?
  • 序列化完全不同。下面的答案证明了这一点,但代码既麻烦又脆弱。使用第三方代码是托管世界的常态,因为代码已经过测试并且可以正常工作。只需使用NuGet 并安装它。这是无痛和简单的。
  • 好的,我将尝试以有意义的方式更改属性,我将使用反射。这是唯一的方法。类型化数据集映射如何工作?你对此有什么想法吗?我只期待这种方式。
【解决方案3】:

我知道这篇文章现在有点老了,但对于正在寻找答案的其他人来说,这就是你可以追求的......只需使用 lambda 表达式

var managers = employees.Select(x => new Manager()
{
    MgrCode = x.ID,
    MgrName = x.Name
}).ToList();

【讨论】:

  • 假设 Manager() 有超过 2 个属性,您的解决方案将意味着该人必须手动键入和分配所有属性,这通常很乏味。
猜你喜欢
  • 1970-01-01
  • 2015-05-06
  • 1970-01-01
  • 2013-01-19
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多