I have a class Test which holds two members, one (arr) which takes a lot of memory and the other (b) which doesn't:
public class Test
{
public Test() {
Arr = new int[100000000];
}
public bool B {get; private set;}
public int[] Arr {get; private set;}
}
Later in my code, I want to store a lambda expression this way:
// `test` has been declared somewhere as an instance of Test
Action lambda = () => {
if (test.B)
// Do things
}
What will be the memory consumption of this closure?
Will it hold the whole Test object in its environment, or only Test.b?
Should I do instead:
var tmpB = test.B;
Action lambda = () => {
if (tmpB)
// Do things
}
Will it hold the whole Test object in its environment, or only Test.b?
Well, it will capture the variable test (by creating a separate class to contain that variable), which in turn has a value which is a reference to the instance of Test.
In other words, a method like this:
public Action Foo()
{
Test test = new Test();
Action printB = () => Console.WriteLine(test.b);
return printB;
}
will be converted into something like this:
public Action Foo()
{
CompiledGeneratedClass tmp = new CompilerGEneratedClass();
tmp.test = new Test();
Action printB = tmp.GeneratedMethod;
return printB;
}
private class CompilerGeneratedClass
{
public Test test;
public void GeneratedMethod()
{
Console.WriteLine(test.b)
}
}
So yes, if you don't want the delegate to effectively keep the instance of Test alive, you should pull out the value of the property first. Note that that has two semantic differences though:
If the value of the property changes in the object, you won't see that in the delegate any more
If the value of test itself changes (e.g. to refer to a different instance of Test) you won't see that in the delegate any more
The closure is going to store the value of the test variable, and the test variable is just a reference to an object of type Test elsewhere in memory, since it's not a struct, and that Test object doesn't actually have an integer array, it just has a reference to a large array stored in yet another location in memory.
Since you're holding onto a reference to that instance of Test, the object won't be eligible for garbage collection for as long as the closure isn't eligible for garbage collection. If you pull the boolean value out of the Test object and close over that, as you showed, then you're no longer referencing theTest object. If, as a result of that, nothing has access to the Test instance, or the contained array, then it would become eligible for garbage collection. If there would still be other code that could access it, then that wouldn't be the case, and there'd be no benefit.
Related
I have a method that takes an Action as a parameter. Actions are stored in a queue and executed when particular resources become available. Before I invoke an Action, I'd like to check if the instance it is a member of is null.
I did a simple test with the following stupid example. The Action invoked successfully after setting the invokee to null, and as expected, I got a NullReferenceException when attempting to access the property on the null invokee. Nothing jumped out at me when examining the Action at runtime that suggested I could determine if its instance was null.
I guess I could pass in the Action and the instance as parameters and test if the instance is null before invoking. Is it possible to test for a null invokee, or is this just a case of bad design on my part?
UPDATE:
I added the line,
if (explosion.Target != null)
to Bazooka.Fire(), to check for a null target, but it is still invoking the delegate in my example.
public void LetsDoThis()
{
var bazooka = new Bazooka();
var rocketLauncher = new RocketLauncher();
bazooka.LockAndLoad(rocketLauncher.BlowStuffUp);
rocketLauncher = null;
bazooka.Fire();
bool wasThisCompletelyAwesome = rocketLauncher.ThisIsAwesome;
}
public class RocketLauncher
{
public void BlowStuffUp()
{
bool stuffIsBlowingUp = true;
}
public bool ThisIsAwesome
{
get
{
return true;
}
}
}
public class Bazooka
{
private List<Action> explosions = new List<Action>();
public void LockAndLoad(Action loadIt)
{
this.explosions.Add(loadIt);
}
public void Fire()
{
foreach (Action explosion in explosions)
if (explosion.Target != null)
explosion.Invoke();
}
}
This won't work.
The Action does not in any way care about the original reference variable you got it from, it makes a copy of the reference value, and thus has its own reference.
Note that this also means that as long as you still have a reference to the delegate, even though you have no other references to the original object, it will still not be eligible for garbage collection.
The .Target property refers to the instance on which the method that the delegate refers to should be invoked, basically the this "parameter" to that method.
Thus, to have a null target you need to get the delegate from a static method, try this in LINQPad:
void Main()
{
Action a = Static.StaticMethod;
(a.Target == null).Dump();
}
public static class Static
{
public static void StaticMethod() { }
}
You can see that the delegate carries its own instance with this LINQPad code:
void Main()
{
Dummy d = new Dummy { Name = "A" };
Action a = d.Method;
d = new Dummy { Name = "B" };
Action b = d.Method;
d = null;
a();
b();
}
public class Dummy
{
public string Name { get; set; }
public void Method()
{
Debug.WriteLine("Name=" + Name);
}
}
The output here will be
Name=A
Name=B
As requested, let me clarify the difference between the instance, the reference, and the variable.
When you construct an object instance, like this:
var rocketLauncher = new RocketLauncher();
What you're doing is calling a method known as a constructor. The return value from this constructor is a reference to the newly constructed object. Basically, it's a pointer, meaning a memory address of where in the memory that object now lives. If it makes it easier to understand the rest of this answer you can consider it to be just a number.
Additionally you declared a variable, rocketLauncher, to hold this reference, this number.
Note that the object is separate from the variable, they're two distinct items. In one place in memory you have an object, in another place you have the variable containing the reference to that object, it's address, that number.
So when you do this:
bazooka.LockAndLoad(rocketLauncher.BlowStuffUp);
Let's simplify it a bit:
Action a = rocketLauncher.BlowStuffUp;
// bazooka.LockAndLoad(a);
let's forget about the part where we call that LockAndLoad method, and look at what happened when we "converted" the method BlowStuffUp into a delegate of type Action.
Basically, two things was "grabbed":
Which method to make the delegate refer to
The object instance on which to call that method
You can liken this to the following code:
MethodReference = rocketLauncher.BlowStuffUp;
object target = rocketLauncher;
// wrap this into a delegate
This now means you have two references to that object, one lives in the rocketLauncher variable, the other now lives inside the delegate.
What you do with that variable does not in any way change the value of that delegate, it still points to the same object as before. Basically it made a copy of that number. That number is still sitting there inside the delegate.
This is pretty much exactly the same as this:
int a = 10;
int b = a;
a = 0;
// b is still 10
So, to conclude, the .Target property of the delegate does not in any way know, or care, about the original variable that you got the delegate from. A copy was made of the reference value from that original variable, into the delegate, and what you do with the variable afterwards makes no difference at all.
So basically:
The instance is the object, it lives somewhere in memory
The reference is basically the address to it, and you can look at it as sort of a number
The variable is one place where you can store that reference
Now, what if you really want to make the delegate depend on the variable, and care about the value it now has, when you get around to calling it?
Well, one way would be to do this:
bazooka.LockAndLoad(delegate
{
if (rocketLauncher != null)
rocketLauncher.BlowStuffUp();
});
This would make an anonymous method, that would capture the variable itself, and then inside that anonymous method you could explicitly check what value the variable has at the time you call the delegate. If this part, about an anonymous method, does not make sense, you should ask another question here (ideally after reading a bit about anonymous methods, captured variables, and looked over some of the existing questions here on SO).
To test out an anonymous method, test the following code in LINQPad:
void Main()
{
object dummy = new object();
Action a = delegate
{
if (dummy != null)
Debug.WriteLine("not null");
else
Debug.WriteLine("null");
};
a();
dummy = null;
a();
}
It will print out:
not null
null
Use the Target property to check:
if(yourAction.Target != null) {
//...
}
Any Delegate type has a property called Target, so you can also use this for other types of delegate.
Update: In fact, when you use your Action to wrap some method of an object, that object won't never be disposed and that means the NullReferenceException can't be thrown in that case Unless you wrap another method of another object and this method has something to do with the null object.
Well, not in any way trying to argue with Lasse's rather detailed response, I would like to throw in my 5 cents on this.
When you invoke the LockAndLoad method of the bazooka class you simply add the method that's passed to the method to the List explosions collection of the bazooka class. Nulling the instantiation of the class whose method you had passed to the LockAndLoad method (in your example 'rocketLauncher') has NO effect on that collection, meaning that the Target property of the specific Action will not become null. You would have to explicitly remove the method from that collection BEFORE nulling the instantiation of the class.
bazooka.Unload(rocketLauncher.BlowStuffUp);
rocketLauncher = null;
Of course this works only if you modified your bazooka class to the following method in your bazooka class:
public void Unload(Action unloadIt)
{
if (explosions.Contains(unloadIt))
explosions.Remove(unloadIt);
}
This may not be what you are hoping for, but I hope it helped anyhow.
I have the following code:
public double CalculateDailyProjectPullForceMax(DateTime date, string start = null, string end = null)
{
Log("Calculating Daily Pull Force Max...");
var pullForceList = start == null
? _pullForce.Where((t, i) => _date[i] == date).ToList() // implicitly captured closure: end, start
: _pullForce.Where(
(t, i) => _date[i] == date && DateTime.Compare(_time[i], DateTime.Parse(start)) > 0 &&
DateTime.Compare(_time[i], DateTime.Parse(end)) < 0).ToList();
_pullForceDailyMax = Math.Round(pullForceList.Max(), 2, MidpointRounding.AwayFromZero);
return _pullForceDailyMax;
}
Now, I've added a comment on the line that ReSharper is suggesting a change. What does it mean, or why would it need to be changed? implicitly captured closure: end, start
The warning tells you that the variables end and start stay alive as any of the lambdas inside this method stay alive.
Take a look at the short example
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
int i = 0;
Random g = new Random();
this.button1.Click += (sender, args) => this.label1.Text = i++.ToString();
this.button2.Click += (sender, args) => this.label1.Text = (g.Next() + i).ToString();
}
I get an "Implicitly captured closure: g" warning at the first lambda. It is telling me that g cannot be garbage collected as long as the first lambda is in use.
The compiler generates a class for both lambda expressions and puts all variables in that class which are used in the lambda expressions.
So in my example g and i are held in the same class for execution of my delegates. If g is a heavy object with a lot of resources left behind, the garbage collector couldn't reclaim it, because the reference in this class is still alive as long as any of the lambda expressions is in use. So this is a potential memory leak, and that is the reason for the R# warning.
#splintor
As in C# the anonymous methods are always stored in one class per method there are two ways to avoid this:
Use an instance method instead of an anonymous one.
Split the creation of the lambda expressions into two methods.
Agreed with Peter Mortensen.
The C# compiler generates only one type that encapsulates all variables for all lambda expressions in a method.
For example, given the source code:
public class ValueStore
{
public Object GetValue()
{
return 1;
}
public void SetValue(Object obj)
{
}
}
public class ImplicitCaptureClosure
{
public void Captured()
{
var x = new object();
ValueStore store = new ValueStore();
Action action = () => store.SetValue(x);
Func<Object> f = () => store.GetValue(); //Implicitly capture closure: x
}
}
The compiler generates a type looks like :
[CompilerGenerated]
private sealed class c__DisplayClass2
{
public object x;
public ValueStore store;
public c__DisplayClass2()
{
base.ctor();
}
//Represents the first lambda expression: () => store.SetValue(x)
public void Capturedb__0()
{
this.store.SetValue(this.x);
}
//Represents the second lambda expression: () => store.GetValue()
public object Capturedb__1()
{
return this.store.GetValue();
}
}
And the Capture method is compiled as:
public void Captured()
{
ImplicitCaptureClosure.c__DisplayClass2 cDisplayClass2 = new ImplicitCaptureClosure.c__DisplayClass2();
cDisplayClass2.x = new object();
cDisplayClass2.store = new ValueStore();
Action action = new Action((object) cDisplayClass2, __methodptr(Capturedb__0));
Func<object> func = new Func<object>((object) cDisplayClass2, __methodptr(Capturedb__1));
}
Though the second lambda does not use x, it cannot be garbage collected as x is compiled as a property of the generated class used in the lambda.
The warning is valid and displayed in methods that have more than one lambda, and they capture different values.
When a method that contains lambdas is invoked, a compiler-generated object is instantiated with:
instance methods representing the lambdas
fields representing all values captured by any of those lambdas
As an example:
class DecompileMe
{
DecompileMe(Action<Action> callable1, Action<Action> callable2)
{
var p1 = 1;
var p2 = "hello";
callable1(() => p1++); // WARNING: Implicitly captured closure: p2
callable2(() => { p2.ToString(); p1++; });
}
}
Examine the generated code for this class (tidied up a little):
class DecompileMe
{
DecompileMe(Action<Action> callable1, Action<Action> callable2)
{
var helper = new LambdaHelper();
helper.p1 = 1;
helper.p2 = "hello";
callable1(helper.Lambda1);
callable2(helper.Lambda2);
}
[CompilerGenerated]
private sealed class LambdaHelper
{
public int p1;
public string p2;
public void Lambda1() { ++p1; }
public void Lambda2() { p2.ToString(); ++p1; }
}
}
Note the instance of LambdaHelper created stores both p1 and p2.
Imagine that:
callable1 keeps a long-lived reference to its argument, helper.Lambda1
callable2 does not keep a reference to its argument, helper.Lambda2
In this situation, the reference to helper.Lambda1 also indirectly references the string in p2, and this means that the garbage collector will not be able to deallocate it. At worst it is a memory/resource leak. Alternatively it may keep object(s) alive longer than otherwise needed, which can have an impact on GC if they get promoted from gen0 to gen1.
For Linq to Sql queries, you may get this warning. The lambda's scope may outlive the method due to the fact that the query is often actualized after the method is out of scope. Depending on your situation, you may want to actualize the results (i.e. via .ToList()) within the method to allow for GC on the method's instance vars captured in the L2S lambda.
You could always figure out with a reasons of R# suggestions just by clicking on the hints like shown below:
This hint will direct you here.
This inspection draws your attention to the fact that more closure
values are being captured than is obviously visibly, which has an
impact on the lifetime of these values.
Consider the following code:
using System;
public class Class1 {
private Action _someAction;
public void Method() {
var obj1 = new object();
var obj2 = new object();
_someAction += () => {
Console.WriteLine(obj1);
Console.WriteLine(obj2);
};
// "Implicitly captured closure: obj2"
_someAction += () => {
Console.WriteLine(obj1);
};
}
}
In the first closure, we see that both obj1 and obj2 are being explicitly captured; we can see this just by looking at the code. For
the second closure, we can see that obj1 is being explicitly captured,
but ReSharper is warning us that obj2 is being implicitly captured.
This is due to an implementation detail in the C# compiler. During
compilation, closures are rewritten into classes with fields that hold
the captured values, and methods that represent the closure itself.
The C# compiler will only create one such private class per method,
and if more than one closure is defined in a method, then this class
will contain multiple methods, one for each closure, and it will also
include all captured values from all closures.
If we look at the code that the compiler generates, it looks a little
like this (some names have been cleaned up to ease reading):
public class Class1 {
[CompilerGenerated]
private sealed class <>c__DisplayClass1_0
{
public object obj1;
public object obj2;
internal void <Method>b__0()
{
Console.WriteLine(obj1);
Console.WriteLine(obj2);
}
internal void <Method>b__1()
{
Console.WriteLine(obj1);
}
}
private Action _someAction;
public void Method()
{
// Create the display class - just one class for both closures
var dc = new Class1.<>c__DisplayClass1_0();
// Capture the closure values as fields on the display class
dc.obj1 = new object();
dc.obj2 = new object();
// Add the display class methods as closure values
_someAction += new Action(dc.<Method>b__0);
_someAction += new Action(dc.<Method>b__1);
}
}
When the method runs, it creates the display class, which captures all values, for all closures. So even if a value isn't used
in one of the closures, it will still be captured. This is the
"implicit" capture that ReSharper is highlighting.
The implication of this inspection is that the implicitly captured
closure value will not be garbage collected until the closure itself
is garbage collected. The lifetime of this value is now tied to the
lifetime of a closure that does not explicitly use the value. If the
closure is long lived, this might have a negative effect on your code,
especially if the captured value is very large.
Note that while this is an implementation detail of the compiler, it
is consistent across versions and implementations such as Microsoft
(pre and post Roslyn) or Mono's compiler. The implementation must work
as described in order to correctly handle multiple closures capturing
a value type. For example, if multiple closures capture an int, then
they must capture the same instance, which can only happen with a
single shared private nested class. The side effect of this is that
the lifetime of all captured values is now the maximum lifetime of any
closure that captures any of the values.
I've come across this tricky issue. See the following code:
public class Foo
public int A { get; set; }
}
static void Main(string[] args)
{
Foo foo1 = new Foo();
Foo foo2 = new Foo();
ChangeObject1(foo1);
ChangeObject2(ref foo2);
Console.WriteLine("Foo1 = " + foo1.A);
Console.WriteLine("Foo2 = " + foo2.A);
Console.ReadKey();
}
static void ChangeObject1(Foo foo)
{
foo.A += 1;
foo = new Foo();
}
static void ChangeObject2(ref Foo foo)
{
foo.A += 1;
foo = new Foo();
}
After some testing, foo1.A is equal to 1, and foo2.A is still equal to 0. To my understanding, this is because the second method has the instance passed in by reference rather than value. This changes the content of the instance, so when a new object is created and assigned in the method, the value is no longer defined.
But thinking about it confuses me as I feel that there should be similar effects in the first method, even if it's passed by value because the content of the instance is still changed outside of the method.
Anyone know whats actually going on? Thanks!
I presume that you're using c# or java. either way, the concept remains the same
when you declare your foo variables
Foo foo1 = new Foo()
you're declaring what is called a reference type. (all objects that represent classes are reference types)
so, the value stored in foo1 isn't really a Foo, but it's a value that holds the memory address of that Foo.
when you call
static void ChangeObject1(Foo foo)
{
foo.A += 1;
foo = new Foo();
}
you're passing a copy of that reference. Therefore, when you do foo.A += 1, you're changing the member value of the object that you've just passed in.
when you do foo = new Foo(); you're only changing the copy of the reference, but not the original object reference itself.
now, when you call
static void ChangeObject2(ref Foo foo)
{
foo.A += 1;
foo = new Foo();
}
you're not passing a copy of the address of your Foo but you're passing the address of the address of your Foo.
you can think of it as though you're passing the original Foo address itself.
So when you call foo = new Foo(), it doesn't matter what you did to foo before that. You've just over-written your address with the address of a brand new Foo object.
if you change your method to look like the following
static void ChangeObject2(ref Foo foo)
{
foo.A += 1;
}
then you should notice the change
Here's a page that explains reference types It's in java, but the same principles apply to c# too
Perhaps if you think about it in terms of a value type instead of a reference type it would help. Take the first example (number in brackets represents a memory address which holds an integer value).
Passing by Value
[0001] -> 5;
When you pass by value, the value 5 is copied to a new memory address
[0002] -> 5;
[0002] -> 10; // changes the value of a new reference
Changing the value of [0002] will not affect [0001].
Passing by Reference
[0001] -> 5;
On the other hand if you pass the address itself (passing with the ref keyword)
[0001] -> 5;
[0001] -> 10; // changes the value of the original reference
You can now change the value the reference is holding.
Reference types instead of holding an integer hold an address to an object. So it stands that if you pass by reference you are passing the 'container' (ie pointer/reference) which holds the value. You can then change what the container holds. If you pass by value, you are taking the value out of the container and passing it, then the original container is out of the picture.
Consider the following code (for simplicity, I did not follow any C# coding rules).
public class Professor
{
public string _Name;
public Professor(){}
public Professor(string name)
{
_Name=name;
}
public void Display()
{
Console.WriteLine("Name={0}",_Name);
}
}
public class Example
{
static int Main(string[] args)
{
Professor david = new Professor("David");
Console.WriteLine("\nBefore calling the method ProfessorDetails().. ");
david.Display();
ProfessorDetails(david);
Console.WriteLine("\nAfter calling the method ProfessorDetails()..");
david. Display();
}
static void ProfessorDetails(Professor p)
{
//change in the name here is reflected
p._Name="Flower";
//Why Caller unable to see this assignment
p=new Professor("Jon");
}
}
As expected the output is :
Before calling the method ProfessorDetails()...
Name =David
After calling the method ProfessorDetails()...
Name =Flower
The call p=new Professor("Jon"); in ProfessorDetails(Professor p) is not effective, even though it is reference type. Why should i still need to use the ref keyword to get the desired result?
Everything is passed by value in C#. However, when you pass a reference type, the reference itself is being passed by value, i.e., a copy of the original reference is passed. So, you can change the state of object that the reference copy points to, but if you assign a new value to the reference you are only changing what the copy points to, not the original reference.
When you use the 'ref' keyword it tells the compiler to pass the original reference, not a copy, so you can modify what the reference points to inside of the function. However, the need for this is usually rare and is most often used when you need to return multiple values from a method.
An example:
class Foo
{
int ID { get; set; }
public Foo( int id )
{
ID = id;
}
}
void Main( )
{
Foo f = new Foo( 1 );
Console.WriteLine( f.ID ); // prints "1"
ChangeId( f );
Console.WriteLine( f.ID ); // prints "5"
ChangeRef( f );
Console.WriteLine( f.ID ); // still prints "5", only changed what the copy was pointing to
}
static void ChangeId( Foo f )
{
f.ID = 5;
}
static void ChangeRef( Foo f )
{
f = new Foo( 10 );
}
You've got pass by reference and reference type mixed up.
By changing p, you're not changing the thing that p points at, but where p itself is pointing at, so to speak. And because p has not been declared as ref, the reference (to the reference type) is passed by value, and the change to p is not reflected in the code calling ProfessorDetails. Changes to the instance p was pointing at are reflected (as that's a reference type). Would Professor have been a value type, not even those changes would be visible in the calling code.
There is a difference between passing a reference and a reference to a reference.
When you pass an object (of a reference type) the callee can modify the object data through the underlying pointer, but if the callee modifies the reference, when the function returns, the caller does not read the changed reference off the stack. The callee can not change which object is referenced.
When you pass an object by reference, the callee receives a reference to the reference. The callee has a pointer to the original reference, so can modify the reference (thereby changing what object the reference points to) in addition to modifying the object the reference points to.
The actual value of p is a reference to the same professor instance as david. Any calls you make on that reference are dereferenced as calls to the same instance as would calls made on david be. However, p is a copy of that reference, it's not the same as david value.
Thus, when you do p = new Professor(), you are changing the value of the reference variable to point to a new instance. However, that does not modify the david reference, which still points to the old instance.
If you were to pass p as ref,the value of p would be a reference to the david reference variable. Modifying it would actually modify the david value to point to a new instance.
Regarding on 'passing a reference type' vs 'passing by ref (by using ref key word)', after my research my take away is this:
If you have a reference type object, and keep this object passing from one method to another, the entire time the objects are pointing to a certain location of the memory. If you work on this object for example by changing the property value, this will cause change to the original object. Think as if, in the different methods you were talking about the same person all the time; and in one method you changed the color of the shirt of that person. So that will cause change in the original person object as well.
But, on your path of jumping from one method to another, if you create a new reference for the object (as you are doing by writing 'p=new Professor("Jon")'), you are basically breaking the link between the object in a new method and the original object. Your 'p' now references to another location in the memory. So whatever change you make in this new location of the memory, it will have no effect whatsoever to the original object. However if you want to change the original object address and have the link, you need to use ref key word. BECAREFUL TO USE THE REF KEYWORD, because once in any method, you make the original address in the memory change to a new address (by using ref keyword), all the changes to the original object done in other methods are now gone.
Every reference type is pass by value to a method call. So you can modify the data inside your instance because it is pointing to the same place, but if you want to modify the instance you should use ref
public class Professor
{
public string _Name;
public Professor(){}
public Professor(string name)
{
_Name=name;
}
public void Display()
{
Console.WriteLine("Name={0}",_Name);
}
}
public class Example
{
static int Main(string[] args)
{
Professor david = new Professor("David");
Console.WriteLine("\nBefore calling the method ProfessorDetails().. ");
david.Display();
ProfessorDetails(ref david);
Console.WriteLine("\nAfter calling the method ProfessorDetails()..");
david. Display();
}
static void ProfessorDetails(ref Professor p)
{
//change in the name here is reflected
p._Name="Flower";
//Why Caller unable to see this assignment
p=new Professor("Jon");
}
}
If I am passing an object to a method, why should I use the ref keyword? Isn't this the default behaviour anyway?
For example:
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t.Something = "Bar";
}
}
public class TestRef
{
public string Something { get; set; }
}
The output is "Bar" which means that the object was passed as a reference.
Pass a ref if you want to change what the object is:
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);
void DoSomething(ref TestRef t)
{
t = new TestRef();
t.Something = "Not just a changed t, but a completely different TestRef object";
}
After calling DoSomething, t does not refer to the original new TestRef, but refers to a completely different object.
This may be useful too if you want to change the value of an immutable object, e.g. a string. You cannot change the value of a string once it has been created. But by using a ref, you could create a function that changes the string for another one that has a different value.
It is not a good idea to use ref unless it is needed. Using ref gives the method freedom to change the argument for something else, callers of the method will need to be coded to ensure they handle this possibility.
Also, when the parameter type is an object, then object variables always act as references to the object. This means that when the ref keyword is used you've got a reference to a reference. This allows you to do things as described in the example given above. But, when the parameter type is a primitive value (e.g. int), then if this parameter is assigned to within the method, the value of the argument that was passed in will be changed after the method returns:
int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10
void Change(ref int x)
{
x = 5;
}
void WillNotChange(int x)
{
x = 10;
}
You need to distinguish between "passing a reference by value", and "passing a parameter/argument by reference".
I've written a reasonably long article on the subject to avoid having to write carefully each time this comes up on newsgroups
In .NET when you pass any parameter to a method, a copy is created. In value types means that any modification you make to the value is at the method scope, and is lost when you exit the method.
When passing a Reference Type, a copy is also made, but it is a copy of a reference, i.e. now you have TWO references in memory to the same object. So, if you use the reference to modify the object, it gets modified. But if you modify the reference itself - we must remember it is a copy - then any changes are also lost upon exiting the method.
As people have said before, an assignment is a modification of the reference, thus is lost:
public void Method1(object obj) {
obj = new Object();
}
public void Method2(object obj) {
obj = _privateObject;
}
The methods above does not modifies the original object.
A little modification of your example
using System;
class Program
{
static void Main(string[] args)
{
TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(t);
Console.WriteLine(t.Something);
}
static public void DoSomething(TestRef t)
{
t = new TestRef();
t.Something = "Bar";
}
}
public class TestRef
{
private string s;
public string Something
{
get {return s;}
set { s = value; }
}
}
Since TestRef is a class (which are reference objects), you can change the contents inside t without passing it as a ref. However, if you pass t as a ref, TestRef can change what the original t refers to. i.e. make it point to a different object.
With ref you can write:
static public void DoSomething(ref TestRef t)
{
t = new TestRef();
}
And t will be changed after the method has completed.
Think of variables (e.g. foo) of reference types (e.g. List<T>) as holding object identifiers of the form "Object #24601". Suppose the statement foo = new List<int> {1,5,7,9}; causes foo to hold "Object #24601" (a list with four items). Then calling foo.Length will ask Object #24601 for its length, and it will respond 4, so foo.Length will equal 4.
If foo is passed to a method without using ref, that method might make changes to Object #24601. As a consequence of such changes, foo.Length might no longer equal 4. The method itself, however, will be unable to change foo, which will continue to hold "Object #24601".
Passing foo as a ref parameter will allow the called method to make changes not just to Object #24601, but also to foo itself. The method might create a new Object #8675309 and store a reference to that in foo. If it does so, foo would no longer hold "Object #24601", but instead "Object #8675309".
In practice, reference-type variables don't hold strings of the form "Object #8675309"; they don't even hold anything that can be meaningfully converted into a number. Even though each reference-type variable will hold some bit pattern, there is no fixed relationship between the bit patterns stored in such variables and the objects they identify. There is no way code could extract information from an object or a reference to it, and later determine whether another reference identified the same object, unless the code either held or knew of a reference that identified the original object.
This is like passing a pointer to a pointer in C. In .NET this will allow you to change what the original T refers to, personally though I think if you are doing that in .NET you have probably got a design issue!
By using the ref keyword with reference types you are effectively passing a reference to the reference. In many ways it's the same as using the out keyword but with the minor difference that there's no guarantee that the method will actually assign anything to the ref'ed parameter.
ref mimics (or behaves) as a global area just for two scopes:
Caller
Callee.
If you're passing a value, however, things are different. You can force a value to be passed by reference. This allows you to pass an integer to a method, for example, and have the method modify the integer on your behalf.