What is Out syntax of C# and what does it actually do?

Hi,

I have this code from Unity in action:

void Update (){
        if (Input.GetMouseButtonDown (0)) {
            Vector3 point = new Vector3 (_camera.pixelWidth / 2, _camera.pixelHeight / 2, 0);
            Ray ray = _camera.ScreenPointToRay (point);
            RaycastHit hit; // From here, I start to confuse with these code....
            if (Physics.Raycast (ray, out hit)) {           //Out, RaycastHit.....
                StartCoroutine (SphereIndicator (hit.point));   //SphereIndicator is a function to create and delete Sphere every 1 second....
            }
        }
    }

I understand everything but start from RaycastHit, everything give me a headache.
Please explain out to me as it give a me a light to understand other code.

It would be great if you help me explain all the code started aat RaycastHit hit :smile:

When you pass a variable to a function - they can be of three types, input, output, and input/output.

Normally you send your data as input, and this requires no special keyword.

If you use the keyword ā€œoutā€ then the function must set a value to that variable, i.e. it must output a value.

If you use the keyword ā€œrefā€ then the function can take a value in that slot, and alter it to a new value.

Here is some pseudo code to explain It a little better,

class StaticFunction
{
static bool DoMath(float a, ref float b, out float c)
{
c=a+b;//we initialize a new value to c using a and b, this value will be assigned to the variable.
b-=a;//we change the value of b, the variable will be updated
if (b>=0.0f) return true;
else return false;
}
static void Main()
{
float a=1.0f;
float b=2.0f;
float c;//This variable has no value assigned to it....
if (DoMath(a,ref b,out c)) Console.Write(a.ToString()+" "+b.ToString()+" "+c.ToString());
else Console.Write("B Is below zero!");
}
}

a is for inputā€¦
b is for input/output
c is for output only!

The main advantage for this is to be able to return multiple values from a single functionā€¦

In the case of the above function which you are trying to get your head around, it returns two values - a bool indicating success/failure and possibly some other data in the Hit variableā€¦
Unity - Scripting API: Physics.Raycast and Unity - Scripting API: RaycastHit have more information on what is being passed whereā€¦ essentially out Hit will return a RaycastHit structure to the Hit variableā€¦

12 Likes

Say that if the Hitā€™s value is 9 then the it will return the 9 to the RaycastHit structure to the Hit variable as well?
Iā€™m still confusing about this.

yes more or less, normally arguments given are used for input, and normally you would not be able to write to a struct passed in that way, but using the out keyword the method can now write to what was passed into that argument.

when you use it with raycast it is writing out to a already existing RaycastHit struct, that you passed in.

Something like that - except the return structure has more than one value - it is essentially a class of information.

Every entry listed in the Variables section of that page will be returned in the Hit Variable, you can access the data like soā€¦

if (Hit.distance<9.0f) return;//or do somethingā€¦

Collider ObjectHitByRay = Hit.collider;//gets the object the ray hitā€¦

int TriIndex = Hit.triangleIndex;//gets the exact tri/poly that the ray collided with, a usage example combined with the Hit.collider example above should be evidentā€¦

BTW - if you want a rather verbose answer on how it works I can give you it, but its roots are buried in C++/C languages and even then the practice was commonly used with assembler before then, without trying to confuse you too much consider this;

class ImaClass
{
static void Function(int a)
{
a=0;
}
}

When you call this function with some code, say like

int b=2048;
ImaClass.Function(b);

The number gets handed to the functionā€¦
But, if you use the ref keyword, instead imagine that the memory location of b gets handed to the function, and any changes made are updated in that variable, rather than the copied version aboveā€¦ Furthermore, if you use the out keyword, then b will be overwritten with the results produced by the function, a little note about out; if, in the function, you do not set a value to the out variable, your code wonā€™t compileā€¦

2 Likes

out and ref are meant as ā€œsafe replacementsā€ for pointers.

int someNumber = 0;

// You don't actually pass someNumber, but create a copy of it.
// Whatever you do with someNumber inside the method
// does not change the value of someNumber here.
FunctionThatDoesSomething(someNumber);

Sometimes thatā€™s a problem. Sometimes you actually want someNumber to change. Well, then you just return it. But what if you canā€™t? Look at the following example.

int FindANumberSomewhere(int number) {
    int ret;
    // Search the number in some kind of datastructure
    // ...
    return ret;
}

Seems fine, but what do you do, if the number isnā€™t found? Return -1? How do you search for -1 then? The way you are forced to deal with this situation in Java is with exceptions. You just throw an exception inside the method and catch it outside. Microsoft drastically improved upon Java and one place where you can see this is here: You donā€™t have to anymore.
You solve the problem as you would in C and C++. You return whether you found the number.

bool FindANumberSomewhere(out int number) {
    bool found;
    // Search the number in some kind of datastructure
    // ...
    return found;
}
// ...
int num = 0;
if(FindANumberSomewhere(out num)) {
    // Do something with the found number
}
else {
    // Handle it not being found
}
// ...

This way is a) much easier to read than producing try/catch (and possibly finally) blocks left and right and b) much faster.

3 Likes

This is a tricky one. Iā€™ll first explain it in terms of just Raycasting in Unity. Then Iā€™ll hit out specifically. Hope fully between the dozen or so explanations youā€™ll find something.

Short answer

out is a way to allow a function to return two values. So a RayCast returns both a bool (did I hit something) and a RayCastHit (what did I hit).

Why have two values instead of rolling both into a single return type? Thatā€™s because you arenā€™t always interested in what a raycast hits. If you donā€™t need a return value you can leave it out. Unity wonā€™t bother calculating the data, making the operation more performant.

Long answer

out and ref both allow a parameter to be passed by reference instead of by value. Normally when a parameter is passed into a function a copy is made on the stack. For value types the whole type is copied. For reference types the reference is copied.

When using out and ref you donā€™t get a copy on the stack. Instead you get a reference back to an earlier location on the stack. This means you can change the value of a value type in an outer function. You can also change where a reference in an outer function points.

The main difference between out and ref is in intent. ref indicates that the parameter has been declared and initialised outside the function, and the function can modify it. out indicates that the parameter has been declared but not initialised outside of the function, the function has the responsibility to initialise it.

out and ref are normally to be avoided. When using out and ref the called function must make assumptions about the calling function. This breaks proper encapsulation, and generally makes things more complex. There are some cases where it makes sense to use it, but tread lightly.

10 Likes

I think I get it 70% but I donā€™t know when Iā€™m going to use it again beside Raycasting because it seem a little bit hard to understand.

Youā€™ll probably use it again, because thereā€™s other parts of the API that use it as well.

Not to resurrect a kinda dead thread, but do these posts still stand true?
I understand how out and ref works but are they really considered bad practice?
How important is proper encapsulation in a C# environment like Unity thatā€™s less modular with itā€™s libraries?
I know good practices are somewhat subjective at times but Iā€™m kinda new to programming in Unity and while I can already see many uses in using out and ref in player behavior functions, Iā€™m not sure if I should start to.

I wouldnā€™t call out/ref bad practice. I would say though that overuse of them would be a code smell to me and Iā€™d investigate the intent of the usages and consider a possible refactoring if it needs it. Case in point the ā€˜Physics.Raycastā€™ method that takes an out parameter of RaycastHit isnā€™t ā€œbadā€, itā€™s a perfectly suitable usage of it.

Important to use encapsulation is up to you. If you want to utilize strong OOP principles, than yeah, itā€™s important. But if you arenā€™tā€¦ then itā€™s not needed. Rather instead of if itā€™s bad/notā€¦ instead consider WHY youā€™re using it or not.

I understand, so for example in my game project,
In my update() I do 4 raycasts to align my character with the ground as typical as that is, I then want to use a function to compute the new up vector, I could be passing the hit data to the function or use a ref to pass the data instead.
Does using ref make a difference here for performance?
Is it proper form even?
Am I even considering it in the proper context?

The primary reason youā€™d use ā€˜refā€™ is to allow the method youā€™ve called the ability to modify what youā€™ve passed in. In the case of structs/value types, it means we can update the struct/value and the calling code has its member updated as well. In the case of a reference type (class), it means the variable will be updated.

Give you an example of a use case for ā€˜refā€™:
Array.Resize

Array.Resize takes in a reference type (an array is a class, and therefore a reference type). The reason it takes it in by ā€˜refā€™ is because itā€™s not actually resizing an array. Itā€™s creating a new array of the size in question, copying the contents of the passed in array to it, and making sure that the variable you passed it in as is updated to this new reference.

This is an example of how ā€˜refā€™ to a reference type is beneficial.

ā€¦

For a value/struct type you could see an example like Interlocked.Increment:

This increments an int/long as an atomic operation. Itā€™s done by ā€˜refā€™ so that the variable you want incremented is done so all self contained in the method as an atomic operation. If it had returned a value the ā€˜settingā€™ of the variable wouldnā€™t be part of the atomic operation and thusly could come out of sync in a threaded situation.

To see what I meanā€¦ when you say any of these:

i++;
i += 1;
i = i + 1;

(note the first 2 are really just syntax sugar for the last one)

What the program actually sees this as, is:

copy i into operating registry r1
copy 1 into operating registry r2
sum r1 and r2 placing result in r1
set i to r1

In IL you can see this:

    IL_0003: ldloc.0      // i
    IL_0004: ldc.i4.1
    IL_0005: add
    IL_0006: stloc.0      // i

(note again, all 3 ways of writing that compile to the same IL)

In a threaded situation if you did i++ simultaneouslyā€¦ both could interweave at any point in that statement chain.

If you have method like so:

int Increment(int value)
{
    return value + 1;
}

i = Increment(i);

What the program actually sees is:

allocate stack frame for function 'Increment'
copy the value of i onto call stack for 'value'
copy value into operating registry r1
copy 1 into operating registry r2
sum r1 and r2 placing result in r1
drop stack frame
place result at position of stack frame
set i to result

Thereā€™s even more odds of interweaving here.

By adding in the ā€˜refā€™, weā€™re no longer copying in the value of i, we instead reference the location of i in memory. Now that Interlocked.Increment has that location/address it can create a lock on that addressā€¦ while itā€™s locked no other lock can be established until it is released. So we result in:

void Increment(ref int value) ...
Increment(ref i);

Is:

allocate stack frame for function 'Increment'
copy the address of i onto call stack for 'value' ('value' and 'i' both point at the same address)
lock the address of 'value'
copy value into operating registry r1
copy 1 into operating registry r2
sum r1 and r2 placing result in r1
set value to r1 (and thusly setting i to r1 as well since they're both the same address)
release lock
drop stack frame

By doing this, we ensure that i isnā€™t volatile for the duration of the operation. Itā€™s all atomic.

If we had returned the result, the operation would become volatile, and we couldnā€™t control the order at which i is set.

1 Like

With those basic examples of use casesā€¦ to your question of:

It can and it canā€™t.

So lets say youā€™re running a 32-bit program and you take in an ā€˜intā€™. Copying an int to the stack frame, or copying the address of an int to the stack frame is basically the same cost since weā€™re talking about 4 bytes of data either way (int is 32-bits, a ref is 32-bits).

If say it was a Vector3, well now a copy is 12 bytes of data, and a ref is 4 bytes. Is there more time to copy 12 bytes vs 4 bytes?

Sure! Itā€™s approximately 3 times longer.

Will you notice???

Noā€¦ likely not. Youā€™d have to be doing this so frequently itā€™s absurd. The frequency would have to be so high that there are most likely going to be other things you could optimize away insteadā€¦ like, inlining the function/method rather than calling/allocating a stack frame for that function/method.

Where itā€™d start getting noticeable is for VERY LARGE structs. But this is why Microsoft suggests you donā€™t create very large structs.

Use ref for what itā€™s intended for. Not for optimization.

ref is used to pass the address of a variable rather than what is in the variable. (address is being used very loosely hereā€¦ managed languages get a little wonky on what an address is)

And this is why I led with an example of ref in use in the .net framework. So you can see why Microsoft chose to use it. It had nothing to do with optimization, and everything to do with what the method is attempting to accomplish.

1 Like

Both of these responses are very much appreciated.
I understand out and ref much better now and the examples
gave me a better perspective on the context they should be implemented in.