How do I copy an instance of an object?

2019-03-15 05:34发布

问题:

I'm trying to write some code that populates a List (actually, it's a series of Lists, but we can pretend it's just one List). The idea is to add an IPackage to the List for the total quantity of IPackage on order. See the following code:

        ParseExcel pe = new ParseExcel();
        Pinnacle p = pe.ParsePinnacleExcel();
        Rack r = new Rack(20,2,4.5,96,42,6,25*12);
        foreach (PinnacleStock ps in p.StockList.Where(x => 
                 x.ColorCode == "10" && 
                 x.PackageLength == 30.64))
        {
            for (int i = 1; i <= ps.OnOrder; i++)
            {
                r.TryAddPackage((IPackage)ps);
            }
        }

Everything seems to be working well, insofar as the IPackage is repeatedly added to the list. However, it seems that the same instance of the object is being added, i.e. the object is not being copied each time it's added to the list.

What do I need to do to ensure that a copy of the object is inserted into the list, and not just an additional reference?

回答1:

Then you need to implement ICloneable and replace

r.TryAddPackage((IPackage)ps);

with

r.TryAddPackage((IPackage)ps.Clone());

It's up to you to decide how Clone should populate the new instance of PinnacleStock that it returns.

At the most basic level, you could say

public PinnacleStock : ICloneable {
    public PinnacleStock Clone() {
        return (PinnacleStock)this.MemberwiseClone();
    }
    object ICloneable.Clone() {
        return Clone();
    }
    // details
}

This will just do a shallow copy of PinnacleStock. Only you know if this is the correct semantics for your domain.



回答2:

If you only need a shallow copy, then you can write a quick-fix clone method:

public class PinnacleStock : ICloneable
{
    public PinnacleStock Clone()
    {
        return (PinnacleStock)this.MemberwiseClone();
    }

    object ICloneable.Clone()
    {
        return Clone();
    }

    // Other methods
}

If you need a deep copy (i.e. if PinnacleStock has sub-objects that you want to be copied as well), then you will need to write one yourself.



回答3:

As others have said, you would need to make that copy, in some PinnacleStock-specific way:

foreach (PinnacleStock ps in p.StockList.Where(x => x.ColorCode == "10" && 
                                                    x.PackageLength == 30.64))
{
  for (int i = 1; i <= ps.OnOrder; i++)
  {
    PinnacleStock clone = ps.CopySomehow();  // your problem
    r.TryAddPackage((IPackage)clone);
  }
}

However, you might want to question whether this is the right solution. Do you really need a separate instance of the PinnacleStock? Does adding a PinnacleStock to a Rack really create a new, independent instance? Are you planning to modify or track each of these individual copies separately? Is it correct that now you will have PinnacleStock instances that don't appear in your StockList? Not knowing your domain or the semantics of PinnacleStock, it's hard to be sure, but you might want to consider, say, creating a PinnacleStockRackEntry object to represent an instance of a PinnacleStock -- depends on the intended semantics of course!



回答4:

You have to supply the logic to copy the object yourself. .Net does not have deep-copy built-in anywhere (with the notable potential exception of serialization). The closest it comes is the MemberwiseClone() method, but even that would copy references for any members of your type that are themselves reference type.



标签: c# .net oop