关于实例化的List的副本

本文关键字:副本 List 实例化 | 更新日期: 2023-09-27 18:16:15

在下面的代码中,为什么y.e[0].k不等于20而不是30?显然正在发生的事情是 y.e[0] = x.e[0]

但是我需要的是当 y 实例化时将列表从 x 复制到 y,而不仅仅是将指针 x.e 复制到 y.e;

但是:正如我所期望的那样,x.c 是真的y.c 是假的。

任何有用的评论表示赞赏

class Eclass {
    public int k { get; set; }
    public Eclass(int iK) { k = iK; }
}
class Sclass {
    private static int ID = -1;
    public long Id { get; set; }
    public bool c { get; set; }
    public List<Eclass> e { get; set; }
    public Sclass() {
        c = false;
        Id = ++ID;
        e = new List<Eclass>();
    }
    public Sclass(Sclass org) {
        c = org.c;
        Id = ++ID;
        //foreach (var OrgE in org.e) { e.Add(OrgE); } //also doesn't work
        //e = org.e.ToList();
        e = new List<Eclass>(org.e);
    }
}
class Program {
    static void Main(string[] args)
    {
        Console.WriteLine("steve");
        Eclass e1 = new Eclass(1);
        Eclass e2 = new Eclass(2);
        Eclass e3 = new Eclass(3);
        Eclass e4 = new Eclass(4);
        Sclass x = new Sclass();
        x.c = false;
        x.c = true;
        x.e.Add(e1);
        x.e.Add(e2);
        Sclass y = new Sclass(x);
        x.e.Add(e3);
        y.e.Add(e4);
        x.e[0].k = 10;
        y.e[0].k = 20;
        x.c = false;
        Debug.WriteLine("//{0}// //{1}// :: //{2}// //{3}//", x.c, x.e[0].k, y.c, y.e[0].k);
        x.c = !x.c;
        y.c = !y.c;
        x.e[0].k = 30;
        Debug.WriteLine("//{0}// //{1}// :: //{2}// //{3}//", x.c, x.e[0].k, y.c, y.e[0].k);
    }
}
//假////20//:://真///

/20//

//True////30//:://False////

30//

关于实例化的List<T>的副本

您使用Sclass复制构造函数创建了y

Sclass y = new Sclass(x);

问题是Sclass复制构造函数中的这一行。这就是y.e的来源:

e = org.e.ToList();

这将创建一个org.e的副本 - 一个新的List<Eclass>,它引用了旧项目中的原始项目。因此,x.e[0]y.e[0] 是同一对象。然后你给y.e,并在e2x.e每个额外的项目,但你从不看那些。

由于Eclass是引用类型而不是值类型,因此需要显式创建列表中每个Eclass实例的副本,而不仅仅是列表本身的副本。如果将Eclassclass更改为struct,则会自动复制,因为那样它就不是引用类型。然后,您应该会看到预期的语义。 Eclass的行为类似于int k:赋值将创建值的新副本,而不是对旧对象的新引用。

然而,更常见的方法是给Eclass一个克隆方法,该方法返回一个新的、相同的Eclass副本——或者给它一个复制构造函数,就像Sclass的那个一样。我更喜欢复制构造函数而不是Clone()因为 .NET 框架倾向于更多地使用它们,在这种情况下,您已经有一个用于Sclass。保持一致。

public Eclass {
    public Eclass() {}
    public Eclass(int k) {
        this.k = k;
    }
    public Eclass(Eclass org) {
        k = org.k;
    }
}

在 Sclass 复制构造函数中,像这样复制 e:

public Sclass(Sclass org) {
    c = org.c;
    Id = ++ID;
    //  For each item in org.e, create an identical copy, and then make a list of those. 
    e = org.e.Select(ec => new Eclass(ec)).ToList();
}

这称为"深层复制":它创建"根"对象的副本,并用它引用的所有引用类型对象的副本填充它。

你现在做的是一个"浅拷贝"。

阅读评论。 您将看到信用属于@EdPlunkett

class Eclass {
    public int k { get; set; }
    public Eclass(int iK) { k = iK; }
    public Eclass(Eclass org) { k = org.k; }
}
class Sclass {
    private static int ID = -1;
    public long Id { get; set; }
    public bool c { get; set; }
    public List<Eclass> e { get; set; }
    public Sclass() {
        c = false;
        Id = ++ID;
        e = new List<Eclass>();
    }
    public Sclass(Sclass org) {
        c = org.c;
        Id = ++ID;
        e = org.e.Select(ec => new Eclass(ec)).ToList();
    }
}
class Program {
    static void Main(string[] args)
    {
        Console.WriteLine("steve");
        Eclass e1 = new Eclass(1);
        Eclass e2 = new Eclass(2);
        Eclass e3 = new Eclass(3);
        Eclass e4 = new Eclass(4);
        Sclass x = new Sclass();
        x.c = false;
        x.c = true;
        x.e.Add(e1);
        x.e.Add(e2);
        Sclass y = new Sclass(x);
        x.e.Add(e3);
        y.e.Add(e4);
        x.e[0].k = 10;
        y.e[0].k = 20;
        x.c = false;
        Debug.WriteLine("//{0}// //{1}// :: //{2}// //{3}//", x.c, x.e[0].k, y.c, y.e[0].k);
        x.c = !x.c;
        y.c = !y.c;
        x.e[0].k = 30;
        Debug.WriteLine("//{0}// //{1}// :: //{2}// //{3}//", x.c, x.e[0].k, y.c, y.e[0].k);
    }
}
相关文章: