简体   繁体   中英

c# object references are not changed

I need to write a wrapper that when the code goes out of current scope, performs something on an existing object.

The code looks like this:

public class ObjWrapper : IDisposable
{
   private KnownType dt = null;

   public ObjWrapper(KnownType data)
   {
      this.dt = data;
   }

   public void Dispose()
   {
      SaveKnownTypeInDB(this.dt);
   }
}

And the call would look like this:

KnownType data = new KnownType();
// do something on `data`

using (ObjWrapper ow = new ObjWrapper(data))
{
  // do something on `data`
}

I'm always getting the values in the database coming from original state of the object. When I am putting a breakpoint inside Dispose(), I can confirm that it has the original values. When I am checking on the stack the caller-method, the object I passed in the constructor has the correct values. I was expecting that the data object is passed by reference and all properties called inside ObjWrapper have the 'updated' values. I tried also to pass the data with ref in the constructor, or to put the Data as a property to ObjWrapper and to set it right after the constructor, but it is all the same. Any ideea why? I thought that for this kind of objects c# is using references ...

Thanks.

Updated

Expected behaviour is that when I put a breakpoint in Dispose I see only initial state of data.

Since KnownType is a class and not a struct , you're always assigning a reference inside of ObjWrapper . As such, any changes you make to data will be reflected inside of the wrapper, as well.

I thought that for this kind of objects c# is using references ...

It is, which is why you're seeing the current state of the object. Since you're storing a reference to the original instance, any changes to the instance will be reflected in that reference as well.

If you want to save a copy of the data, you'll need to make the copy yourself. This will likely mean copying the values manually.

So much of the problem is still based on code you haven't shown, but there is now enough to make an educated guess.

I will assume that KnownType is a class. Being a class it is a reference type. That means that any variable (such as data ) doesn't actually contain whatever data a KnownType object contains; the variable will just contain a reference (aka a location in memory) of an actual KnownType object that exists "somewhere else".

When you pass the KnownType object to the constructor, you're passing it by value, but that value is just a reference. This means you're making a copy of the reference. The two references both point to the same actual object. Because of this, if the actual object that they both point to is changed, both variables "see" the change. However, if you change which object either variable points to, the other variable will have no idea about that change. Therefore, if you aren't seeing your changes reflected in the database save what it means is that rather than mutating the existing data object inside of the using, you're assigning an entirely new KnownType object to that variable. (You haven't shown this code, so it's hard to say that with certainty.)

So, the first solution would just be don't do that . Don't change what data points to, just mutate the data object that you passed into the ObjWrapper .

If it's real important that you be able to assign a new KnownType to data and have that new one be the one that is saved, you could do something like this (I don't suggest it if you can avoid it though, as it's rather odd semantically, making it bug prone.)

public class ObjWrapper : IDisposable
{
    private Func<KnownType> functor;
    public ObjWrapper(Func<KnownType> functor)
    {
        this.functor = functor;
    }
    public void Dispose()
    {
        SaveKnownTypeInDB(functor());
    }
}

and to use it:

KnownType data = new KnownType();
// do something on `data`

using (ObjWrapper ow = new ObjWrapper(() => data))
{
    // do something on `data`
}

You need to understand the difference between Reference and Value types. You are not passing a copy of the object, you are passing a reference to the object. I would guess that you need to implement/invoke a deep copy mechanism in the constructor of your ObjWrapper class

you are supposed to implement Dispose() method to release the resources. If you are not calling Dispose() method explicitly, you can't determine when it will be executed. It is advised not to write any logic or code other than resource releasing.

I have found the reason why this was happening:

  • the initial state of the object (before entering in constructor) was close to 'new' (most of its members, well, at least of those visible in watch window were with the 'default' values);
  • inside the scope of the wrapper, the object was passed to various functions. One of them, based on some logic, decided to 'recreate' the object and populate it's properties with ones from another object;
  • of course, this means that the reference for the original object has changed inside the wrapper's scope;
  • outside, in wrapper class', the 'reference' was still 'pointing' to old object.

This is normal and expected behaviour in the light of the new facts. I want to thank you all for your help and suggestions!

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