将对象复制到对象(使用Automapper吗?)


77

我有一堂课:

public class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

我有两个Person实例(person1和person2)。我想将person2的内容复制到person1。我想在一个指令中制作此副本,而不是按属性制作:

person1.LastName = person2.LastName;

在文档中,我看到将一个对象复制到另一个对象,但是类型不同。类型相同时如何复制对象?


17
@Darin-这将创建引用,而不是副本。
Steven Ryssaert 2011年

1
@Uw Concept,是的,但是由于问题不是很清楚,我想我可能建议这样做。
Darin Dimitrov

3
不想创建参考,但想要完全独立的副本
Kris-I

6
我建议不要为此使用AutoMapper-它不是用于克隆项目的(尽管在某些情况下可能会工作)。相反,该BinaryFormatter技巧可以发挥魔力,并且可以轻松地封装在扩展方法中。
Jimmy Bogard

3
从概念上讲,不,它们不是相同的操作。克隆还涉及私有数据,而不仅仅是公共数据。基本上,克隆仅查看私有字段,而映射则不会。
Jimmy Bogard 2014年

Answers:


84

据我了解的问题,OP不想将person2克隆到Person的新实例中,而是在询问如何将person2的内容复制到Person的现有实例中person1)。AutoMapper的Mapper.Map方法的重载可以为您完成此操作:

Mapper.CreateMap<Person, Person>();
Mapper.Map<Person, Person>(person2, person1);
//This copies member content from person2 into the _existing_ person1 instance.

注1: @alexl的答案创建了Person实例。如果您对person1指向的实例有其他引用,那么如果将person1变量重定向到新实例,这些引用将不会(大概)获得所需的数据更新。

注意2:您需要注意,(递归)复制深度取决于AutoMapper在映射时知道的映射!
如果Person类的成员属于Brain类,并且您另外Mapper.CreateMap<Brain, Brain>();在复制数据Mapper.Map<Person, Person>(person2, person1);调用之前完成了操作,则person1将保留其当前Brain实例,但此Brain将接收person2Brain实例的成员值。那是你的深复制
但是,如果AutoMapper在复制之前没有Brain-Brain映射,则person1Brain成员将引用与person2引用的同一Brain实例。那就是你会得到一个浅表副本。 这将递归地应用于所有成员,因此您最好确保AutoMapper具有要深度复制的成员类的映射,而没有要深度复制的成员类的映射。

使用AutoMapper的替代方法是使用使用反射的方法。(请注意,链接中的代码会进行浅表复制!)

在AutoMapper版本0.2中添加了“支持填充现有对象,而不是由AutoMapper自己创建目标对象” 。


这似乎在对象级别上起作用,但是其他对象作为属性是通过引用复制的。也许有一种方法可以告诉automapper克隆属性,而不是复制ref?
音速灵魂

30

既然您问过With Automapper?我是否可以建议您不要使用AutoMapper?

而是使用MemberwiseClone()一种Clone方法,例如

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person Clone()
    {
        return (Person) MemberwiseClone();
    }
}

更新

重要的是要注意,这并不能达到原始海报复制到的愿望。person1 person2

但是,(和@Jimmy Bogard指出的)MemberwiseClone()如果只需要复制(克隆)对象,则首选使用。

例如,如果您这样做:

//I need a copy of person1 please! I'll make a new person object 
//and automapper everything into it!
var person2 = new Person2();
Mapper.Map<Person, Person>(person1, person2)

那么您应该/可以使用

//oh wait, i can just use this!
var person2 = person1.Clone()

4
唯一的问题是您现在声明可以克隆您的“人员”类型。从语义上讲这是不正确的,目标是执行从一个实例到另一个实例的复制(不是克隆)。
肖恩·威尔逊

您能否详细说明与该特定示例之间的差异?
2013年

6
wal:MemberwiseClone创建一个的Person实例。OP希望保留Person1指向的现有实例,并用Person2的值填充它。如果将Person1重定向到新实例,可能还有其他对Person1指向的实例的引用将无法获得所需的数据更新。
UlfÅkerstedt13年

7
object.MemberwiseClone()执行浅拷贝,顺便说一下,不是深拷贝。 google.ch/…–
Errore Fatale

1
您没有解释为什么不使用AutoMapper?我是这里的一个人,认为这return (Person) MemberwiseClone();是一种不好的做法还是仅仅是一种“代码异味”?
Geka P

20
Mapper.CreateMap<Person, Person>();

// Perform mapping

var person1 = Mapper.Map<Person, Person>(person2);

希望这可以帮助。


17
这不是Mapper.Map<Person, Person>(person2, person1);吗?您的方式将创建一个新对象person1(我在回答中被杀死了);)
wal 2013年

2
对于在这里使用google的用户:此处删除了Map.CreateMap,并在此处介绍了配置映射的新方法stackoverflow.com/a/38194308/4547594
Igand,

Automapper是仅复制简单属性还是复制其他导航属性?如何告诉它仅复制简单属性而不复制对象属性?
Naomi

2

为什么要为此使用Automapper?一个简单的克隆将为您完成这项工作。

在此处阅读更多内容:深度克隆对象


7
因为AutoMapper使用的反射比二进制序列化要快。
huysentruitw 2014年

4
并且AutoMapper不需要将涉及的所有类型都标记为[可序列化]。AutoMapper也可以配置;如果您只想复制某些字段,或做某种转换作为复制的一部分,则可以这样做。:-)
Jonathan Gilbert

8
一个“简单的”深克隆?我们对简单有不同的定义。
Gusdor

3
@WouterHuysentruit Automapper还允许您对映射进行单元测试。如果您在以后的生活中修改类型,那就太好了。
Gusdor

2

在当前版本的AutoMapper中,不能使用静态AutoMapper.Mapper.Map方法。而是像这样初始化一个新的映射器:

var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Person, Person>();
});

var mapper = new Mapper(config);

var clone = mapper.Map<Person>(person);

通常,您需要在Startup.cs文件中注册映射程序以进行依赖项注入,然后将其注入业务类中:

public void ConfigureServices(IServiceCollection services)
{
    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Person, Person>();
    });

    var mapper = new Mapper(config);

    services.AddSingleton(mapper);

    // ...
}

重要: 请勿在您的实体类中创建或注入映射器!

当然,您应该更喜欢MemberwiseClone()在简单的情况下使用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.