I cannot understand references!

VitzzViperzz

Well-known member
Joined
Jan 16, 2017
Messages
75
Location
United Kingdom
Programming Experience
1-3
Hello,

So I as I have have been working on more complicated C# topics and I cannot seem to grasp some things as easily as I used to.

One of those topics are references!

I think I may have asked this question before but not asked it properly.

Can someone explain what they are used for with some code explanation?

I have looked at these two slices of code but I just cannot understand what is going on.

C#:
public void ModifyAnIntAndButton(ref int value, ref Button button) 
{    

int i = value;    
i *= 5;    
value = i - 3;    
button = button1; 

}

private void button2_Click(object sender, EventArgs e) 
{  
 
 int q = 100;    
Button b = button1;   
 ModifyAnIntAndButton(ref q, ref b);   
Console.WriteLine("q = {0}, b.Text = {1}", q, b.Text); 

}

C#:
static void addOneToRefParam ( ref int i )  
{  

i = i + 1;  
Console.WriteLine ( "i is : " + i ) ; 

}

test = 20 ; 

addOneToRefParam(ref test); 
Console.WriteLine ( "test is : " + test ) ;

i is : 21
test is : 21

How?
 
You're not really asking about references in general but rather about passing method arguments by reference, which is just a part of the whole reference story. Passing method arguments can be discussed in isolation by focusing on the results rather than the underlying mechanism, which is what I'll do here.

Method arguments are passed by value by default but can be specified as being passed by reference. You can use the 'out' or 'ref' keyword to declare a method argument as being passed by reference. By default, method parameters are used to pass data into a method only. If you declare a parameter using 'out' then that parameter is for passing data out of the method only. If you declare a method using 'ref' then that parameter can be used to pass data into and/or out of the method. That's really all there is to it. Check out this example:
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int valVariable = 1;
            int outVariable = 2;
            int refVariable = 3;

            Console.WriteLine("valVariable = {0}; outVariable = {1}; refVariable = {2}", valVariable, outVariable, refVariable);

            ExampleMethod(valVariable, out outVariable, ref refVariable);

            Console.WriteLine("valVariable = {0}; outVariable = {1}; refVariable = {2}", valVariable, outVariable, refVariable);
            Console.ReadLine();
        }

        private static void ExampleMethod(int valParameter, out int outParameter, ref int refParameter)
        {
            //Console.WriteLine("valParameter = {0}; outParameter = {1}; refParameter = {2}", valParameter, outParameter, refParameter);
            Console.WriteLine("valParameter = {0}; refParameter = {1}", valParameter, refParameter);

            valParameter = 100;
            outParameter = 200;
            refParameter = 300;

            Console.WriteLine("valParameter = {0}; outParameter = {1}; refParameter = {2}", valParameter, outParameter, refParameter);
        }
    }
}

That code creates three variables, calls the method and passes the variables as arguments and sets the three parameters inside the method.

The first point to note is each of the Console.WriteLine statements and the fact that one has been commented out. If you uncomment that line you will see that it generates a compilation error. That's because, as I said, a parameter declared 'out' is used only for passing data out of the method so the compiler won't even let you try to use it's value until you've set it. If you don't set the 'out' parameter somewhere in the method then that will also generate a compilation error.

If you run that code and check the output you'll see that the first line outputs the initial values of the three variables, just as you'd expect. The second line outputs the values passed in to the method, except for the 'out' parameter that cannot be used at the stage. The third line outputs the values that you have just assigned to the parameters just as you'd expect. The fourth line is the interesting one.

The fourth line shows that the value of the first variable, which was passed to the method by value, has remained unchanged, even though the corresponding parameter was modified inside the method. The values of the second and third variables, which were passed by reference, have been changed, reflecting the changes made to the corresponding parameters inside the method. THAT is the point. When you set a parameter inside a method, the original variable will be affected if and only if that parameter is passed by reference.

So, all you really have to remember is:

1. If you want to pass data into a method and you don't want changes made to the parameter to affect the original variable then pass the parameter by value, which is the default and will be used in the vast majority of cases.
2. If you want to pass data out of a method only then declare the parameter 'out'. In that case, any original value you pass in will be ignored. A perfect example is any TryParse method.
3. If you want to optionally pass data into a method and optionally get a new value back out, declare the parameter 'ref'.
 
You're not really asking about references in general but rather about passing method arguments by reference, which is just a part of the whole reference story. Passing method arguments can be discussed in isolation by focusing on the results rather than the underlying mechanism, which is what I'll do here.

Method arguments are passed by value by default but can be specified as being passed by reference. You can use the 'out' or 'ref' keyword to declare a method argument as being passed by reference. By default, method parameters are used to pass data into a method only. If you declare a parameter using 'out' then that parameter is for passing data out of the method only. If you declare a method using 'ref' then that parameter can be used to pass data into and/or out of the method. That's really all there is to it. Check out this example:
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int valVariable = 1;
            int outVariable = 2;
            int refVariable = 3;

            Console.WriteLine("valVariable = {0}; outVariable = {1}; refVariable = {2}", valVariable, outVariable, refVariable);

            ExampleMethod(valVariable, out outVariable, ref refVariable);

            Console.WriteLine("valVariable = {0}; outVariable = {1}; refVariable = {2}", valVariable, outVariable, refVariable);
            Console.ReadLine();
        }

        private static void ExampleMethod(int valParameter, out int outParameter, ref int refParameter)
        {
            //Console.WriteLine("valParameter = {0}; outParameter = {1}; refParameter = {2}", valParameter, outParameter, refParameter);
            Console.WriteLine("valParameter = {0}; refParameter = {1}", valParameter, refParameter);

            valParameter = 100;
            outParameter = 200;
            refParameter = 300;

            Console.WriteLine("valParameter = {0}; outParameter = {1}; refParameter = {2}", valParameter, outParameter, refParameter);
        }
    }
}

That code creates three variables, calls the method and passes the variables as arguments and sets the three parameters inside the method.

The first point to note is each of the Console.WriteLine statements and the fact that one has been commented out. If you uncomment that line you will see that it generates a compilation error. That's because, as I said, a parameter declared 'out' is used only for passing data out of the method so the compiler won't even let you try to use it's value until you've set it. If you don't set the 'out' parameter somewhere in the method then that will also generate a compilation error.

If you run that code and check the output you'll see that the first line outputs the initial values of the three variables, just as you'd expect. The second line outputs the values passed in to the method, except for the 'out' parameter that cannot be used at the stage. The third line outputs the values that you have just assigned to the parameters just as you'd expect. The fourth line is the interesting one.

The fourth line shows that the value of the first variable, which was passed to the method by value, has remained unchanged, even though the corresponding parameter was modified inside the method. The values of the second and third variables, which were passed by reference, have been changed, reflecting the changes made to the corresponding parameters inside the method. THAT is the point. When you set a parameter inside a method, the original variable will be affected if and only if that parameter is passed by reference.

So, all you really have to remember is:

1. If you want to pass data into a method and you don't want changes made to the parameter to affect the original variable then pass the parameter by value, which is the default and will be used in the vast majority of cases.
2. If you want to pass data out of a method only then declare the parameter 'out'. In that case, any original value you pass in will be ignored. A perfect example is any TryParse method.
3. If you want to optionally pass data into a method and optionally get a new value back out, declare the parameter 'ref'.

Hey, thanks for the reply.

I think that has helped clear it up a little bit more.

I will just have to keep working on them until it finally clicks.
 
If you're still having a bit of trouble, try this analogy.

You have a carton of eggs and you want to know how many you'll have left if you take six out. You count them and write the number down then do the math on paper - that's like passing by value.
If instead you grab the carton and take six eggs out, that's like passing by reference. You end up with the same number but doing it by value doesn't change the number of eggs in the carton.

So by value (default) is like writing the number down and doing it on paper, not touching the carton.
C#:
myEggCount = SubtractEggsFromCarton(carton);

private int SubtractEggsFromCarton(int eggsInCarton)
{
    eggsInCarton -= 6
    return eggsInCarton;
}
// myEggCount = 6 carton = 12
But by Reference is like taking eggs out of the carton.
C#:
myEggCount = SubtractEggsFromCarton(carton);

private int SubtractEggsFromCarton(ref int eggsInCarton)
{
    eggsInCarton -= 6
    return eggsInCarton;
}

// myEggCount = 6 carton = 6

Although named differently, eggsInCarton and carton are the same variable when passed by reference. Anthing done to eggsInCarton is actually being done to carton
 
If you're still having a bit of trouble, try this analogy.

You have a carton of eggs and you want to know how many you'll have left if you take six out. You count them and write the number down then do the math on paper - that's like passing by value.
If instead you grab the carton and take six eggs out, that's like passing by reference. You end up with the same number but doing it by value doesn't change the number of eggs in the carton.

So by value (default) is like writing the number down and doing it on paper, not touching the carton.
C#:
myEggCount = SubtractEggsFromCarton(carton);

private int SubtractEggsFromCarton(int eggsInCarton)
{
    eggsInCarton -= 6
    return eggsInCarton;
}
// myEggCount = 6 carton = 12
But by Reference is like taking eggs out of the carton.
C#:
myEggCount = SubtractEggsFromCarton(carton);

private int SubtractEggsFromCarton(ref int eggsInCarton)
{
    eggsInCarton -= 6
    return eggsInCarton;
}

// myEggCount = 6 carton = 6

Although named differently, eggsInCarton and carton are the same variable when passed by reference. Anthing done to eggsInCarton is actually being done to carton

Hey, that's a very interesting way to look at it. The explanation helped me understand it more actually - thank you for that.

Just late to reply as I was doing other things, but thanks.
 
Hey, that's a very interesting way to look at it. The explanation helped me understand it more actually - thank you for that.

Just late to reply as I was doing other things, but thanks.

No problem. What actually happens in the two methods of passing is this:

By Value - copies the value of the supplied variable into the local variable
By Reference - assigns the local variable's memory address to that of the supplied variable so you're just using a different name for the same memory location.

In C/C++ they refer to this as using pointers. A Pointer is a special type of variable that will only hold addresses of other variables. There are lots of tricks you can do using pointers but you can very quickly get yourself into trouble if you don't completely understand what you're doing. I tried C decades ago and although I understood what pointers were, just kept getting things messed up when it came to having pointers to pointers etc. I find higher level languages much easier to stay in control of.

So why would you use a reference rather than a value? Well, one reason would be for speed. If you're passing a large amount of data (array, list, class, etc) rather than a simple int or something then it can take quite a while (in computer terms) to copy all that data into the local variable. If you pass by reference then you aren't copying anything, you're just sending the 32/64 bit address which is really fast. You just have to be fully aware that anything you do to the local variable is actually done to the passed variable. Also, if you pass by ref then assign to a local variable, then you may as well have just passed by value since it'll take just as long to copy everything.

Most of the time by value is really all you need.
 
Rhodan said:
So why would you use a reference rather than a value? Well, one reason would be for speed. If you're passing a large amount of data (array, list, class, etc) rather than a simple int or something then it can take quite a while (in computer terms) to copy all that data into the local variable. If you pass by reference then you aren't copying anything, you're just sending the 32/64 bit address which is really fast. You just have to be fully aware that anything you do to the local variable is actually done to the passed variable. Also, if you pass by ref then assign to a local variable, then you may as well have just passed by value since it'll take just as long to copy everything.
Value types and reference types is a different kind of discussion. Objects that you mention are reference types and are always passed as references, but the reference itself can also be passed as value or reference. That is pretty much the "pointer to pointer" distress you had :) For example, you have an object (A) with a property of some other object type (B), if you pass the object of that property (ie the object B) then the receiving method can modify that object. No matter if passed by value or reference A will see the changes to object B. The difference is here if method can assign a new object (B#2), passed by value it can not, but passed by reference it can.
An abstract example:
    class A
    {
        public B Sample { get; set; }
    }

    class B
    {
        public int ID { get; set; }
    }

        static void ByValue(B input)
        {
            input.ID = 1;
            input = new B() { ID = 2 };
        }

        static void ByReference(ref B input)
        {
            input.ID = 1;
            input = new B() { ID = 2 };
        }

            B someB = new B();
            A someA = new A() { Sample = someB };            

            ByValue(someB);
            var same = (someA.Sample == someB); // true
            var value = someA.Sample.ID; // 1
            

            ByReference(ref someB);
            same = (someA.Sample == someB); // false, someB is now different object
            value = someA.Sample.ID; // 1
            var valueB = someB.ID; // 2
 
Back
Top Bottom