简体   繁体   中英

C# simplify and perhaps generalize my object cloning method

I've written a method:

class CopyableFloatCommand : FloatCommand
{
    public CopyableFloatCommand DeepCopy(LocationHeaderDTO locHeader, string commandId,
        List<FloatProductDetailsDTO> recountProuducts)
    {
        var newCommand = (CopyableFloatCommand)MemberwiseClone();
        newCommand.Location = locHeader ?? newCommand.Location;
        newCommand.CommandId = commandId ?? newCommand.CommandId;
        newCommand.RecountProducts = recountProuducts ?? newCommand.RecountProducts;
        return newCommand;
    }
}

And am then calling it via:

_tCheckinCommand = _pTCommand.DeepCopy(stagingLocHeadDto, SCICommand,
    new List<FloatProductDetailsDTO>(_pTCommand.MoveProducts));

In order to deepcopy an object of type FloatCommand.

As the MemberwiseClone() is a protected method, it's got to be called the way you see above - one cannot parse in a FloatCommand type in the method parameter and call it via fc.MemberwiseClone(), for example. As my method ought to work on a FloatCommand type, I've created a new nested class CopyableFloatCommand which inherits from FloatCommand. DeepCopy method then shallow clones the FloatCommand, casts to the child type and changes some properties as/when needed.

Creating a new class specifically for this purpose seems a bit clunky and I didnt' see a more obvious way of writing it at the time. In terms of lines-of-code, would there be a simpler way of employing a deepcopy such as the above? What about if another class, UserCommand, attempted to deepcopy a User object? UserComand would be a sibling to FloatCommand such that they both inherit from Command. The method would have different parameters parsed for the different types (although I can just remove the parameters altogether and use the instance variables if need be) as the different sub-types have slightly different properties.

In light of this is there a more generic method of writing the DeepCopy method, to be available for access for all the Command types in order to avoid some code duplication, given the above constraints?

Thanks!

I think you're suspecting that the responsibility of cloning the object and mutate its state after it is cloned should be separated - since you're facing with the similar task again (i mean UserCommand).

I would do the following in this situation:

Create a mutation interface:

public interface ICopyCommandMutation
{
  void Mutate(Command target); 
}

For the sake of extensability i would create the default muate implementation:

public class NoMutation : ICopyCommandMutation
{
  public void Mutate(Command target) {}
}

Create the CopyableCommand class and move the DeepCopy() method there (you should also inherit FloatCommand from CopyableCommand):

public CopyableCommand : Command 
{
   public CopyableCommand DeepCopy(ICopyCommandMutation commandMutation = null)
   {
      var newCommand = (CopyableCommand)MemberwiseClone();
      if (commandMutation == null) commandMutation = new NoMutation();
      commandMutation.Mutate(newCommand);
      return newCommand;
   }
}

Now all the CopyableCommand inheritors can be copied with 'mutations' - you just need to implement the class. For example the FloatCommand 'mutations' from your question:

public class ChangeLocationRecountProducts : ICopyCommandMutation
{
  // these fields should be initialized some way (constructor or getter/setters - you decide
  LocationHeaderDTO locHeader;
  string commandId;
  List<FloatProductDetailsDTO> recountProducts;

  public void Mutate(Command floatCommand)
  {
     var fc = floatCommand as FloatCommand;
     if (fc == null) { /* handle problems here */ }
     fc.Location = locHeader ?? fc.Location;
     fc.CommandId = commandId ?? fc.CommandId;
     fc.RecountProducts = recountProuducts ?? fc.RecountProducts;
  }
}

Here is the usage:

var clrp = new ChangeLocationRecountProducts();
// ... setting up clrp
_tCheckinCommand = _pTCommand.DeepCopy(clrp);

Now if you need to 'mutate' the UserCommand - you can do the separate mutation class for it and keep the mutation logic there. The ability to make different mutations in different sutations (just by defining the separate mutation classes) comes for free. The only problem i can see here - is that you probably cannot create CopyableCommand and inherit other commands from it (3rd party library?). The solution would be to use Castle dynamic proxy.

I haven't used the Automapper but i suspect that it is doing something similar.

The solution is not 'lines-of-code optimal' - but you would benefit from it if you have to mutate large number of command classes when copying instances.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM