In C#, what are some rules of thumb for _where_ to clone objects?

438 views Asked by At

Here's the situation: I am trying to determine the where part of cloning an object to avoid modifying the original.

I have two options:

  • Clone the object in the caller and pass the cloned object to a method ("callee"), thus preventing potential modification by the callee.
  • Clone the object in the callee because the callee modifies the object it is passed, this making the assumption that the caller never wants the argument object to be modified.

I found this 6-year-old answer with a variety of opinions. Unfortunately it doesn't seem there was a real consensus.

Passing copy of object to method -- who does the copying?

Here's my question in code-form:

  • Do I clone the object in the caller and pass the cloned object to the method?
public static void Main()
{
    var foo = new Foo();
    Bar.Baz(foo.DeepClone());
}

public static class Bar
{
    public static void Baz(Foo foo)
    {
        /* ... modifies members in foo ... */
    }
}

public class Foo { /* ... */ }
  • Do I clone the object in the callee?
public static void Main()
{
    var foo = new Foo();
    Bar.Baz(foo);
}

public static class Bar
{
    public static void Baz(Foo foo)
    {
        foo = foo.DeepClone();
        /* ... modifies members in foo ... */
    }
}

public class Foo { /* ... */ }

So, my questions are:

  • What are some good rules of thumb for where to clone objects across languages, but especially in C# and .NET-land?

  • Regardless of the answer(s), what are some good ways to document the behavior of methods that modify arguments, or methods that clone objects?

2

There are 2 answers

6
usr On BEST ANSWER

Is the purpose of the method to mutate the object? Then don't clone inside of the method. You want the side-effects to happen. Usually, the method name would clearly call out the fact that a mutation is expected (e.g. UpdateCustomer).

If it is not the explicit purpose of the method to mutate its inputs then the mutation is an implementation detail and the method must see to it that the mutation does not leak out. It can do that by cloning.

Methods should not use their inputs as mere scratch space. Some in the Win32 APIs do that which is horribly confusing.

1
Yaur On

The best way to enforce (and document) constness is to define a read only interface and define your parameter as that interface. Anything that accepts the interface is constant and anything that accepts the full object might mutate the object.

If you are following this approach, the caller should be cloning if it does not want the side effects since we have given permission to the callee to modify the object by passing it a modifiable object.