10

What is the proper way to implement assignment by value for a reference type? I want to perform an assignment, but not change the reference.

Here is what I'm talking about:

void Main()
{
    A a1 = new A(1);
    A a2 = new A(2);
    a1 = a2; //WRONG: Changes reference
    a1.ValueAssign(a2); //This works, but is it the best way?
}

class A
{
    int i;

    public A(int i)
    {
        this.i = i;
    }

    public void ValueAssign(A a)
    {
        this.i = a.i;
    }
}

Is there some sort of convention I should be using for this? I feel like I'm not the first person that has encountered this. Thanks.

EDIT:

Wow. I think I need to tailor my question more toward the actual problem I'm facing. I'm getting a lot of answers that do not meet the requirement of not changing the reference. Cloning is not the issue here. The problem lies in ASSIGNING the clone.

I have many classes that depend on A - they all share a reference to the same object of class A. So, whenever one classes changes A, it's reflected in the others, right? That's all fine and well until one of the classes tries to do this:

myA = new A();

In reality I'm not doing new A() but I'm actually retrieving a serialized version of A off the hard drive. But anyways, doing this causes myA to receive a NEW REFERENCE. It no longer shares the same A as the rest of the classes that depend on A. This is the problem that I am trying to address. I want all classes that have the instance of A to be affected by the line of code above.

I hope this clarifies my question. Thank you.

James Jones
  • 8,653
  • 6
  • 34
  • 46
  • You probably want to clone the object, try http://stackoverflow.com/questions/78536/cloning-objects-in-c/ – blu Jun 30 '09 at 20:02
  • Cloning the object and assigning it will also change the reference, so this will not work. – James Jones Jun 30 '09 at 20:15
  • based on the updates to the question, agreed. – blu Jun 30 '09 at 21:44
  • Could you describe your problem? I have a similar pattern with a configuration object, and I get by just having all readers reload the reference every time they need to grab a setting, admittedly a lame approach. – Anton Tykhyy Jul 01 '09 at 07:00
  • I have several classes that share a cache. I need a cache because fetching the data is extremely expensive. Several classes share this cache and one of them needs to perform "cache = (Cache)getSerializedCacheFromHardDrive()" This is the same thing as "cache = new Cache()". The classes that share the cache do not know about each other so it would require some cumbersome workarounds notify everyone that it changed, just as you're describing. – James Jones Jul 01 '09 at 12:40
  • How are you notifying now? (or how are you planning to do it?) – vgru Jul 01 '09 at 17:00
  • I work around it now using ValueAssign. By assigning the fields individually the reference never changes, so I don't have to notify anyone of the reference changing. Perhaps I'm already using the best approach? – James Jones Jul 01 '09 at 17:28
  • How about some Method Cache.RefreshCacheFromHDD() that does the same thing, but keeps the object as it is? – DasKrümelmonster Jun 25 '13 at 21:57
  • I'm working on a VERY similar problem... http://stackoverflow.com/questions/31707418/comparing-two-objects-and-setting-them-equal-without-breaking-entity-reference – user2245175 Jul 30 '15 at 15:38

12 Answers12

6

It sounds like you're talking about cloning. Some objects will support this (via ICloneable) but most won't. In many cases it doesn't make sense anyway - what does it mean to copy a FileStream object? ICloneable is generally regarded as a bad interface to use, partly because it doesn't specify the depth of the clone.

It's better to try to change your way of thinking so this isn't necessary. My guess is that you're a C++ programmer - and without wishing to cast any judgements at all: don't try to write C# as if it's C++. You'll end up with unidiomatic C# which may not work terrible well, may be inefficient, and may be unintuitive for C# developers to understand.

One option is to try to make types immutable where possible - at that point it doesn't matter whether or not there's a copy, as you wouldn't be able to change the object anyway. This is the approach that String takes, and it works very well. It's just a shame that there aren't immutable collections in the framework (yet).

In your case, instead of having the ValueAssign method, you would have WithValue which would return a new instance with just the value changed. (Admittedly that's the only value available in your case...) I realise that this sort of copying (of all but the property that's about to change) goes against what I was saying about copying being somewhat unidiomatic in C#, but it's within the class rather than an outside body deciding when to copy.

I suspect I'm not explaining this terribly well, but my general advice is to design around it rather than to try to explicitly copy all over the place.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • If the object was immutable, all instances of classes that depend on that immutable object would not be able to manipulate the immutable object without losing their reference to it. – James Jones Jun 30 '09 at 20:43
  • If the WithValue function creates a 'new' instance, that's a 'new' reference that would not relate back to existing references. – James Jones Jun 30 '09 at 20:44
  • Indeed - which would be the point. Everything could be confident that what it had wouldn't change. This is very much the functional way of looking at programming. – Jon Skeet Jun 30 '09 at 21:09
  • Well what I have HAS to change, and then everyone would need to be notified about the change. Putting it that way, it sounds like I would have to utilize the Observer pattern, which I think is cumbersome compared to the alternatives. Someone suggested the observer pattern below and I gave them +1 but I think that would be overkill. – James Jones Jul 01 '09 at 12:31
2

I believe you should be using a struct instead of a class than, as structs work by value and not by reference.

MiffTheFox
  • 21,302
  • 14
  • 69
  • 94
  • This class contains functions so it's not as easy to just switch it to a struct, but I will keep this in mind. – James Jones Jun 30 '09 at 20:14
  • @James - A struct can contain methods just as easily as a class. – Andrew Hare Jun 30 '09 at 20:15
  • 1
    Woah there... structs are very rarely the right choice, and should almost always be immutable... – Marc Gravell Jun 30 '09 at 20:20
  • Woops. Had my mind in C mode. Anyways, structs in C# are value types and therefore a single instance of the struct could not be referenced by multiple objects. – James Jones Jun 30 '09 at 20:49
  • @AndrewHare: Note quite as easily. The only way to restrict a struct method to use on mutable storage locations is to use the rather awkward `public static void DoSomething(ref it); ... MyStruct.DoSomething(ref someExpression);`. If a method which should only be used on mutable storage locations is written using instance-method syntax, the compiler won't squawk if it's used on a read-only location or temporary value, but will instead silently generate bogus code. – supercat May 17 '13 at 19:35
2

For what you want to do, I think A.ValueAssign(otherA) is the best way.

Given that you want to have one reference of A around, ensuring that the reference isn't destroyed is key.

Wouldn't you also be served by using a singleton pattern here as well?

Irwin
  • 12,551
  • 11
  • 67
  • 97
  • It's funny you mention that. I just got done refactoring it to NOT be a singleton so that it would be more compatible with TDD. In my case, A is a cache that needs to be shared by multiple classes. I read somewhere that singletons should only offer services, and that it's bad to have a singleton that holds a state since it's basically a global variable at that point. It actually made more sense when I started to enter "singleton hell" in my testing. I love singleton, but it was just bringing me down. – James Jones Jul 01 '09 at 12:19
1

One approach is to use a copy constructor. e.g.,

MyClass orig = ...; MyClass copy = new MyClass(orig);

Where you copy the elements of MyClass. Depending on how many reference types the class contains this might involve recursive use of copy constructors.

Shea
  • 11,085
  • 2
  • 19
  • 21
  • Sorry, this answer does not apply to the problem. Obtaining a copy is not the issue at hand. – James Jones Jun 30 '09 at 20:51
  • Have you considered the Observer/Observable pattern? Instead of multiple classes sharing a reference to A they could instead handle events from A (e.g., OnPropertyChanged). When you deserialized A you could copy it's invocation list to maintain the connection... – Shea Jul 01 '09 at 02:54
  • I am familiar with the Observer pattern and I think it would work, but I would not prefer to use it since it is rather cumbersome compared to the problem I am addressing. I give you +1 but I'm not marking this as the best answer. – James Jones Jul 01 '09 at 12:24
1

Others have suggested cloning in their answer, but that's only part of the deal. You also want to use the results of a (possibly deep) clone to replace the contents of an existing object. That's a very C++-like requirement.

It just doesn't come up very often in C#, so there's no standard method name or operator meaning "replace the contents of this object with a copy of the contents of that object".

The reason it occurs so often in C++ is because of the need to track ownership so that cleanup can be performed. If you have a member:

std::vector<int> ints;

You have the advantage that it will be properly destroyed when the enclosing object is destroyed. But if you want to replace it with a new vector, you need swap to make that efficient. Alternatively you could have:

std::vector<int> *ints;

Now you can swap in a new one easily, but you have to remember to delete the old one first, and in the enclosing class's destructor.

In C# you don't need to worry about that. There's one right way:

List<int> ints = new List<int>();

You don't have to clean it up, and you can swap in a new one by reference. Best of both.

Edit:

If you have multiple "client" objects that need to hold a reference to an object and you want to be able to replace that object, you would make them hold a reference to an intermediate object that would act as a "wrapper".

class Replaceable<T>
{
    public T Instance { get; set; }
}

The other classes would hold a reference to the Replaceable<T>. So would the code that needs to swap in a replacement. e.g.

Replaceable<FileStream> _fileStream;

It might also be useful to declare an event, so clients could subscribe to find out when the stored instance was replaced. Reusable version here.

You could also define implicit conversion operators to remove some syntax noise.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • But the thing is, I _can't_ change the reference. That breaks the tie to the other classes that have that old reference. – James Jones Jun 30 '09 at 20:54
  • Hmm.. An intermediate object and implicit conversion operator to basically overload the assignment operator so that converting from T to Replaceable would be seamless. I get you. That would most certainly work. +1. That's basically a broken down version of Observer. Unfortunately, in the scope of my the API I'm developing, using your pattern would force my users make a "Replaceable cache". If they decide to use cache for multiple purposes and do something like "cache = null" they would be confused when all the objects depending on cache suddenly didn't have a cache anymore. – James Jones Jul 01 '09 at 18:19
  • cache = null would only affect one client, and could be fixed by making cache readonly, or giving it a private setter. cache.Instance = null would affect all users of the same cache, but you could make Instance have a setter that throws if the new value is null, making that mistake very easy to trap. – Daniel Earwicker Jul 01 '09 at 19:23
  • I spoke with a couple potential users of my API and they agree that working with intermediate objects is not very intuitive/desirable, especially considering the alternatives. – James Jones Jul 06 '09 at 12:47
  • Be interested to know what alternatives you're considering – Daniel Earwicker Jul 06 '09 at 13:55
1

We have cases where we do exactly what you are talking about. We have many objects referencing a particular instance of an object and we want to change the instance of the object so that every object referencing that existing instance see the change.

The pattern we follow is almost what you have - just the names are different:

    class A
    {
        int i;
        public A(int i)
        {
            this.i = i;
        }
        public void Copy(A source)
        {
            this.i = source.i;
        }
    }
Joe Erickson
  • 7,077
  • 1
  • 31
  • 31
1

In several WinForms based applications, I've needed similar functionality, in my case to allow a data entry form to work on a copy of the object, information from which is copied onto the original object only if the user elects to save the changes.

To make this work, I brought across an idea from my Delphi days - the Assign() method.

Essentially, I wrote (well, ok, generated) a method that copies across properties (and list contents, etc etc) from one instance to another. This allowed me to write code like this:

var person = PersonRespository.FindByName("Bevan");
...
var copy = new Person();
copy.Assign(person);
using (var form = new PersonDataEntryForm(copy))
{
    if (form.ShowAsModelessDialog() == MessageReturn.Save)
    {
        person.Assign(copy);
    }
}

Changes made within the dialog are private until the user chooses to save them, then the public variable (person) is updated.

An Assign() method for Person might look like this:

public void Assign(Person source)
{
    Name = source.Name;
    Gender = source.Gender;
    Spouse = source.Spouse;

    Children.Clear();
    Children.AddRange( source.Children);
}

As an aside, having an Assign() method makes a copy-constructor almost trivially easy to write:

public Person(Person original)
    : this()
{
    Assign(original);
}
Bevan
  • 43,618
  • 10
  • 81
  • 133
  • I'm more interested in the Assign() method you're talking about. I have never used Delphi so I do not understand what you are implying. I envisioned the "holy grail" answer to involve Reflection or something crazy that I wouldn't dare use. I can only hope that's where you're heading with your answer. – James Jones Jul 01 '09 at 12:58
  • It's certainly possible to write a version of Assign() that uses reflection, and therefore allows almost property value assignment between almost any types, but performance may be an issue. In my case, my Assign() methods were simple code. I'll amend my answer to clarify. – Bevan Jul 01 '09 at 20:06
  • Your Assign method is doing exactly what I proposed in my question. (Not that this is a bad thing.. +1) I'm glad we're on the same wavelength with the Reflection thing. In an ideal world, I think that there would be a special operator for this type of value assignment, since it is possible with reflection. It's a shame that C# lacks this feature. – James Jones Jul 01 '09 at 20:57
  • The problem with introducing a special operator is that there are too many variations on possible semantics. E.g.: Should properties with private setters be set? How do you handle read/only properties? What about properties typed as IList/IEnumerable - do you share the list or make a copy? If a copy, which implementation of the interface, and what do you do about any hooked up events? Who owns objects within a list or sequence - should they be shared or copied? Essentially you need to know the "Bounded Context" (Eric Evans) for the object, which is a domain decision, not a compiler one. – Bevan Jul 08 '09 at 21:18
1

I wish there was a "second best" answer option, because anyone who mentioned Observer deserves it. The observer pattern would work, however it is not necessary and in my opinion, is overkill.

If multiple objects need to maintain a reference to the same object ("MyClass", below) and you need to perform an assignment to the referenced object ("MyClass"), the easiest way to handle it is to create a ValueAssign function as follows:

public class MyClass
{
    private int a;
    private int b;

    void ValueAssign(MyClass mo)
    {
        this.a = mo.a;
        this.b = mo.b;
    }
}

Observer would only be necessary if other action was required by the dependent objects at the time of assignment. If you wish to only maintain the reference, this method is adequate. This example here is the same as the example that I proposed in my question, but I feel that it better emphasizes my intent.

Thank you for all your answers. I seriously considered all of them.

James Jones
  • 8,653
  • 6
  • 34
  • 46
  • I can totally see where you are coming from with this question. In my case client app holds a bunch of refs to an object - but that object gets updated over the wire so update comes in on a fresh object. I dont wont/cant re point all refs to the new object - I just have to update the existing object by VALUE to the object that has come accross the wire. Im suprised this situation is not more common. – Ricibob Dec 08 '11 at 12:02
  • Curious, why implement this with a function, and not a copy constructor? – Mac Sigler Jun 08 '13 at 17:19
  • Wow, can't believe this was 4 years ago.. Value assignment isn't very useful in a constructor since that would imply creating a new reference of the object being copied-to. The goal of value assignment is to preserve the existing reference, not create a new one. – James Jones Jun 10 '13 at 14:28
1

I had the exact same problem. The way I solved it was by putting the object that everything is referencing inside another object and had everything reference the outer object instead. Then you could change the inner object and everything would be able to reference the new inner object.

OuterObject.InerObject.stuff
EpiX
  • 1,281
  • 2
  • 16
  • 22
0

If I got it right, you are talking about proper Singleton deserialization.

  1. If you are using .Net native serialization then you might take a look at the MSDN ISerializable example. The example shows exactly that - how to override ISerializable.GetObjectData to return the same instance on each call.

  2. If you are using Xml serialization (XmlSerializer), then you manually implement IXmlSerializable in your object's parent class, and then take care to get a single instance each time.

  3. A simplest way would be to ensure this in your parent property's setter, by accessing some kind of a static cache. (I find this pretty dirty, but that's an easy way to do it).

For example:

 public class ParentClass
 {
      private ReferencedClass _reference;
      public ReferencedClass Reference
      {
          get
          { 
              return _reference;
          }
          set
          {
              // don't assign the value, but consult the
              // static dictionary to see if we already have
              // the singleton
              _reference = StaticCache.GetSingleton(value);
          }
      }
 }

And then you would have a static class with some kind of a dictionary where you could quickly retrieve the singleton instance (or create it if it doesn't exist).

Although this may work for you, I also agree with the others that this is rarely the best (or only) way to do it. There is surely a way to refactor your code so that this becomes unnecessary, but you should provide some additional info about what is the intended usage, where is this data accessed from, or simply why do classes really need to reference a single object.

[Edit]

Since you are using a static cache similar to a singleton, you should take care to implement it properly. That means several things:

  1. Your cache class should have a private constructor. You don't want anyone to create a new instance explicitly - this is not an option when using singletons. So this means you should expose some public static property like Cache.Instance which will always return the reference to the same private object. Since the constructor is private, you are sure that only Cache class can create the instance during initialization (or first update). Details on implementing this pattern can be found at http://www.yoda.arachsys.com/csharp/singleton.html (which is also a great thread-safe implementation).

  2. When all you object have the same instance, then you can simply notify cache to update the single private instance (e.g. call Cache.Update() from somewhere). This way you can update the only instance which everyone is using.

But it still not clear from your example how exactly you are notifying your clients that the data has been updated anyway. An event-driven mechanism would work better, as it would allow you to decouple your code - Singletons are evil.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • That is a very interesting approach since I already have the "static class where I could quickly retrieve the singleton instance". Please see my comments on the question for a more detailed description of my individual problem. – James Jones Jul 01 '09 at 12:45
  • Yeah, singletons are not good. I understand that. I refuse to use them in my code unless it's absolutely appropriate. However, I do have one AVAILABLE to the user's of my API since they said "Hey James, I keep losing scope of the cache and I would like a way to get the cache back." So I gave them a singleton to access it. However, I refuse to base my API around using the singleton because of the downfalls (which we will not get into here). – James Jones Jul 01 '09 at 18:37
  • My code is in fact decoupled since nobody has to be notified when one of them changes the cache. They simply all maintain the same reference to a single object, and it works! I think I may have had my answer from the start. – James Jones Jul 01 '09 at 18:41
  • The difference with a proper implementation of Singleton pattern is that by having a private constructor for your object, you *prevent* yourself (or anyone else) from *ever* creating a new instance. This may not seem seem important if it is a small project only you are working on, but why not do it properly? And I assure you, singleton == coupled code, even if it may seem different from your perspective (google it, there are lots of examples and articles). – vgru Jul 01 '09 at 19:13
  • Singleton != coupled code. My API does not use the singleton, so the singleton cannot induce coupling upon the API. The singleton merely exists for the user of the API. I want, and need, to create multiple instances of the cache, especially for testing purposes. Making the constructor private and basing all of my code off the singleton is definitely not the way to go in my case. – James Jones Jul 01 '09 at 19:21
  • Perhaps I am using the wrong terminology? What do you call a singleton with a public constructor? I should probably be saying "single instance of a class" pattern.. or something... – James Jones Jul 01 '09 at 19:51
0

It's not possible to overload the assignment operator in c# as it would be in c/c++. However even if that was an option I'd say your trying to fix the symptom not the problem. Your problem is that the assignment of a new reference is breaking code, why not just assign the read values to the original reference? and If you are affraid that others might new and assign make the object into a singleton or similar so that it can't be altered after creation but the reference will stay the same

Rune FS
  • 21,497
  • 7
  • 62
  • 96
  • I used to be in that exact mindset (the singleton idea) until I realized singletons are essentially global variables. In fact, I recently refactored it to NOT be a singleton because of the problems it was causing during my testing. Don't get me wrong, I love singleton, but it has its place, and that is for objects that do not retain a state (or have limited form of state). – James Jones Jul 01 '09 at 12:50
  • I don't like singletons my self. Im currently working very hard to get the Devs on my project to stop spamming singletons to be honest. I used it to describe what I ment. There are other ways to enforce the rule of just one instance of a given type for as you say singletons can be a nightmare when it comes to testing (not limited to testing) – Rune FS Jul 01 '09 at 21:26
0

What if you did this:

public class B
{
    B(int i) {A = new A(i);}
    public A A {get; set;}
}

...

void Main()
{
    B b1 = new B(1);
    A a2 = new A(2);
    b1.A = a2;
}

Throughout your program, references to A should only be accessed via an instance of B. When you reassign b.A, you're changing the reference of A, but that doesn't matter because all of your external references are still pointing to B. This has a lot of similarities with your original solution, but it allows you to change A any way you want without having to update its ValueAssign method.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315