Context
public class Person : IDisposable
{
public Data _data;
public Person() { _data = new Data(); }
public void Dispose() { _data = null; }
public Data GetData()
{
// Return a copy of the reference hoping that _data exists as long as we have a reference to it
// (principle or garbage collection)
return _data; // Or are we actually returning a copy of _data ??????
}
}
Question
Tell me where I'm wrong, and answer the "why".
Data data; // Allocate memory for a reference to a Data
Person person; // Allocate memory for a reference to a Person
using (person = new Person())
{
data = person.GetData(); // We receive the copy of the "reference to the instance" living in the person
}
Console.WriteLine("perons._data null: " person._data); // The instance of Data has indeed been freed by the Dispose
Console.WriteLine("data not null: " data); // WHY is data not null even though the Dispose has been called
I'm wondering that because of that stack overflow answer stating that:
The value being returned is not the list, but a reference to the list object, because List is a reference type
Note
Same result if we replace GetData()
by:
public Data GetNewData()
{
Data data = new Data(); // Create a new instance of Data
// Return a copy of the reference hoping that data exists as long as we have a reference to it
return data;
}
Second question
If indeed the instance of Data
is copied (returned by value) when we return if from GetData()
, then what does a ref return do?
CodePudding user response:
To answer the first question: "WHY is data not null even though the Dispose has been called"
The Dispose sets the local reference to null, the copy of the reference is still in tact.
The _data
will be freed when there isn't any rooted reference, meaning no actually running code uses this reference.
See Understanding Garbage Collection in .NET for more information about garbage collection.
A ref return returns the reference itself. So it isn't a copy. Meaning if you set the local reference to null, the other reference will be null as well
CodePudding user response:
This gets a bit complicated in terminology: Instances of classes are returned by reference, but the reference itself is returned by value.
Therefore, in your snippet:
Data data; // Allocate memory for a reference to a Data
Person person; // Allocate memory for a reference to a Person
using (person = new Person())
{
data = person.GetData(); // **copy** the **reference** from person._data to data
}
Console.WriteLine("perons._data null: " person._data); // the member of the class has been nullified. This does **not** affect the copy of the reference
Console.WriteLine("data not null: " data); // The copy of the reference has not been touched
Even though person._data
was nullified, the object it points to was and is not eligible for garbage collection, since you still have a reference to it from the local variable data
. If Data
was itself disposable and would be disposed, using it would likely throw a ObjectDisposedException
but there's no guarantee or enforcement that a disposed object is in fact ready for garbage collection.
Using ref return
is a really advanced topic (and rarely used) but in this case, it would solve your problem, because if data
is declared as ref Data
it would be null after the dispose.
CodePudding user response:
Your question body contains a different question than your question title. Let me answer the question in the body first.
A few pictures might help understand this. Let's step through your code and have a look at the situation in memory:
Data data;
Person person;
Local variables Objects
--------------- -------
person: null
data: null
using (person = new Person()) {
Local variables Objects
--------------- -------
-------------
person: ------------------> | Person |
data: null ------------- ------
| _data -----|--> | Data |
------------- ------
data = person.GetData();
Local variables Objects
--------------- -------
-------------
person: ------------------> | Person |
data: ---- ------------- ------
| | _data -----|--> | Data |
| ------------- ------
| ^
--------------------------------------
} // end of using - calls Dispose, sets person._data to null
Local variables Objects
--------------- -------
-------------
person: ------------------> | Person |
data: ---- ------------- ------
| | _data: null | | Data |
| ------------- ------
| ^
--------------------------------------
Hence, person._data
is null, but data
isn't.
For completeness, let me also answer the question in your title:
Does
return new MyType()
return a reference to or a copy of MyType.
Neither. new MyType()
always creates a new object (those boxes on the right-hand side of my drawings) and returns a reference to that object.